API reference
Platform administration
Organizations, plans, regions and locations, for platform administrators.
These routes check scopes in the platform organization, not in an organization in the path.
GET /v1/platform/analytics
Traffic across every customer deployment, with the busiest organizations.
Auth: user access token or platform agent key · Scope: platform:analytics in the platform organization
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
range | "24h" | "7d" | "30d" | "90d" | No | "24h" | |
app | `` | No |
Response 200
{
top: {
hosts?: {
key: string
requests: number
bytes: number
name?: string
}[]
deployments?: {
key: string
requests: number
bytes: number
name?: string
}[]
orgs?: {
key: string
requests: number
bytes: number
name?: string
}[]
paths?: {
key: string
requests: number
bytes: number
name?: string
}[]
countries?: {
key: string
requests: number
bytes: number
name?: string
}[]
projects?: {
key: string
requests: number
bytes: number
name?: string
}[]
}
unattributed: number
updatedAt: null | number
range: "24h" | "7d" | "30d" | "90d"
step: number
from: string
to: string
totals: {
bytes: number
requests: number
s2xx: number
s3xx: number
s4xx: number
s5xx: number
hits: number
misses: number
p50: null | number
p95: null | number
}
series: {
bytes: number
requests: number
s2xx: number
s3xx: number
s4xx: number
s5xx: number
hits: number
misses: number
p50: null | number
p95: null | number
t: string
}[]
}GET /v1/platform/mail/usage
Mail sent and received per organization over the last days.
Auth: user access token or platform agent key · Scope: platform:analytics in the platform organization
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
days | integer | No | 7 | 1–31; coerced from a string |
Response 200
{
days: number
total: {
sent: number
received: number
bounced: number
complained: number
}
orgs: {
orgId: string
sent: number
received: number
bounced: number
complained: number
}[]
}GET /v1/platform/usage
Cost per organization for a month (month=YYYY-MM, default this month) from Cost Explorer, grouped by the si:org cost allocation tag, with each organization's Serverless App Services, repositories, databases and buckets, deployment requests and bytes, and mail sent and received in the month. Spend without the tag is untagged.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Query parameter | Type | Required | Notes |
|---|---|---|---|
month | string | No | matches ^\d{4}-(0[1-9]|1[0-2])$ |
Response 200
{
month: string
currency: string
estimated: boolean
costError?: string
orgs: {
projects: number
repositories: number
databases: number
buckets: number
requests: null | number
bytes: null | number
mailSent: null | number
mailReceived: null | number
orgId: string
name: string
slug: string
kind: "platform" | "customer"
planId?: string
cost: null | number
}[]
untagged: null | number
otherOrgs: null | number
}POST /v1/platform/coverage/runs
Records a test coverage run of the platform's repository: its commit, branch, pipeline run and each workspace's tests, line, branch and function coverage, least covered and untested files. Totals are worked out from the workspaces. Kept 90 days. Needs platform:coverage (the nightly pipeline's key).
Auth: user access token or platform agent key · Scope: platform:coverage in the platform organization
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
version | 1 | Yes | |
commit | string | No | matches ^[0-9a-f]{7,64}$ |
branch | string | No | 1–255 characters |
runUrl | string | No | up to 2,000 characters; URL |
source | "ci" | "local" | Yes | |
startedAt | integer | Yes | > 0 |
finishedAt | integer | Yes | > 0 |
packages | object[] | Yes | 1–300 items |
packages[].name | string | Yes | 1–214 characters |
packages[].dir | string | Yes | up to 200 characters; matches ^[A-Za-z0-9][A-Za-z0-9._-]*(\/[A-Za-z0-9._-]+)*$ |
packages[].status | "passed" | "failed" | "error" | Yes | |
packages[].tests | object | Yes | |
packages[].tests.total | integer | Yes | 0–100000000 |
packages[].tests.passed | integer | Yes | 0–100000000 |
packages[].tests.failed | integer | Yes | 0–100000000 |
packages[].tests.skipped | integer | Yes | 0–100000000 |
packages[].tests.todo | integer | Yes | 0–100000000 |
packages[].tests.cancelled | integer | Yes | 0–100000000 |
packages[].durationMs | integer | Yes | 0–100000000 |
packages[].lines | object | Yes | |
packages[].lines.total | integer | Yes | 0–100000000 |
packages[].lines.covered | integer | Yes | 0–100000000 |
packages[].branches | object | Yes | |
packages[].branches.total | integer | Yes | 0–100000000 |
packages[].branches.covered | integer | Yes | 0–100000000 |
packages[].functions | object | Yes | |
packages[].functions.total | integer | Yes | 0–100000000 |
packages[].functions.covered | integer | Yes | 0–100000000 |
packages[].files | object | Yes | |
packages[].files.loaded | integer | Yes | 0–100000000 |
packages[].files.source | integer | Yes | 0–100000000 |
packages[].leastCovered | object[] | Yes | up to 25 items |
packages[].leastCovered[].path | string | Yes | 1–500 characters |
packages[].leastCovered[].lines | object | Yes | |
packages[].leastCovered[].lines.total | integer | Yes | 0–100000000 |
packages[].leastCovered[].lines.covered | integer | Yes | 0–100000000 |
packages[].leastCovered[].branches | object | Yes | |
packages[].leastCovered[].branches.total | integer | Yes | 0–100000000 |
packages[].leastCovered[].branches.covered | integer | Yes | 0–100000000 |
packages[].leastCovered[].functions | object | Yes | |
packages[].leastCovered[].functions.total | integer | Yes | 0–100000000 |
packages[].leastCovered[].functions.covered | integer | Yes | 0–100000000 |
packages[].untested | string[] | Yes | up to 50 items; each 1–500 characters |
packages[].error | string | No | up to 2,000 characters |
packages[].cached | boolean | No | |
totals | any JSON | No |
Response 201
{
runId: string
}Errors
| Status | Message |
|---|---|
503 | The table is busy; try again. |
GET /v1/platform/coverage/latest
The latest coverage run's id, commit and start, so a pipeline can skip a commit it already reported. Needs platform:coverage or platform:admin.
Auth: user access token or platform agent key · Scopes: platform:coverage (for the pipeline's key), platform:admin (for Admin)
Response 200
{
run: null | {
runId: string
commit: null | string
startedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Missing scope … |
GET /v1/platform/coverage
Coverage runs of the last 90 days (newest first) with their totals, a run's workspaces (run=, default the latest) and the workspaces of the run before it, for changes.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Query parameter | Type | Required | Notes |
|---|---|---|---|
run | string | No | up to 64 characters |
Response 200
{
runs: {
branch?: string
source: "local" | "ci"
startedAt: number
commit?: string
finishedAt: number
totals: {
lines: {
total: number
covered: number
}
branches: {
total: number
covered: number
}
functions: {
total: number
covered: number
}
tests: {
total: number
passed: number
failed: number
skipped: number
todo: number
cancelled: number
}
durationMs: number
packages: number
failedPackages: number
}
runUrl?: string
runId: string
}[]
run: null | {
branch?: string
source: "local" | "ci"
startedAt: number
commit?: string
finishedAt: number
totals: {
lines: {
total: number
covered: number
}
branches: {
total: number
covered: number
}
functions: {
total: number
covered: number
}
tests: {
total: number
passed: number
failed: number
skipped: number
todo: number
cancelled: number
}
durationMs: number
packages: number
failedPackages: number
}
runUrl?: string
runId: string
}
packages: {
error?: string
name: string
status: "error" | "failed" | "passed"
durationMs: number
files: {
loaded: number
source: number
}
lines: {
total: number
covered: number
}
dir: string
tests: {
total: number
passed: number
failed: number
skipped: number
todo: number
cancelled: number
}
cached?: boolean
branches: {
total: number
covered: number
}
functions: {
total: number
covered: number
}
}[]
previous: null | {
runId: string
packages: {
error?: string
name: string
status: "error" | "failed" | "passed"
durationMs: number
files: {
loaded: number
source: number
}
lines: {
total: number
covered: number
}
dir: string
tests: {
total: number
passed: number
failed: number
skipped: number
todo: number
cancelled: number
}
cached?: boolean
branches: {
total: number
covered: number
}
functions: {
total: number
covered: number
}
}[]
}
}Errors
| Status | Message |
|---|---|
404 | No such run |
GET /v1/platform/coverage/runs/:runId/package
One workspace of a coverage run (dir=services/api) with its least covered loaded files and the source files no test loaded.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:runId | Coverage run id (cov_…). |
| Query parameter | Type | Required | Notes |
|---|---|---|---|
dir | any JSON | Yes |
Response 200
{
package: {
lines: {
total: number
covered: number
}
branches: {
total: number
covered: number
}
functions: {
total: number
covered: number
}
name: string
dir: string
status: "error" | "failed" | "passed"
tests: {
total: number
passed: number
failed: number
skipped: number
todo: number
cancelled: number
}
durationMs: number
files: {
loaded: number
source: number
}
leastCovered: {
lines: {
total: number
covered: number
}
branches: {
total: number
covered: number
}
functions: {
total: number
covered: number
}
path: string
}[]
untested: string[]
error?: string
cached?: boolean
}
}Errors
| Status | Message |
|---|---|
404 | No such workspace in this run |
GET /v1/platform/mirage/social/settings
Mirage's social limits and ranking weights: what's stored, the settings in force and the defaults. Platform staff (platform:admin).
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
stored: {
postLength?: number
mediaPerPost?: number
imageBytes?: number
videoBytes?: number
editWindowMinutes?: number
editsPerPost?: number
postsPerHour?: number
followsPerDay?: number
maxFollowing?: number
fanoutMaxFollowers?: number
fanoutBatch?: number
feedDays?: number
storyHours?: number
notificationDays?: number
ranking?: {
follows: number
sharesServer: number
affinity: number
affinityCap: number
engagement: number
likedByFollowee: number
popular: number
notInterestedAuthor: number
notInterestedTag: number
halfLifeHours: number
}
blockedWords?: string[]
}
settings: {
postLength: number
mediaPerPost: number
imageBytes: number
videoBytes: number
editWindowMinutes: number
editsPerPost: number
postsPerHour: number
followsPerDay: number
maxFollowing: number
fanoutMaxFollowers: number
fanoutBatch: number
feedDays: number
storyHours: number
notificationDays: number
ranking: {
follows: number
sharesServer: number
affinity: number
affinityCap: number
engagement: number
likedByFollowee: number
popular: number
notInterestedAuthor: number
notInterestedTag: number
halfLifeHours: number
}
blockedWords: string[]
}
defaults: {
postLength: number
mediaPerPost: number
imageBytes: number
videoBytes: number
editWindowMinutes: number
editsPerPost: number
postsPerHour: number
followsPerDay: number
maxFollowing: number
fanoutMaxFollowers: number
fanoutBatch: number
feedDays: number
storyHours: number
notificationDays: number
ranking: {
follows: number
sharesServer: number
affinity: number
affinityCap: number
engagement: number
likedByFollowee: number
popular: number
notInterestedAuthor: number
notInterestedTag: number
halfLifeHours: number
}
blockedWords: string[]
}
updatedAt: null | number
updatedBy: null | string
}PUT /v1/platform/mirage/social/settings
Changes social settings (any of postLength, mediaPerPost, imageBytes, videoBytes, editWindowMinutes, editsPerPost, postsPerHour, followsPerDay, maxFollowing, fanoutMaxFollowers, fanoutBatch, feedDays, storyHours, notificationDays, ranking, blockedWords; null restores a default). Applies within a minute. Audited. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
stored: {
postLength?: number
mediaPerPost?: number
imageBytes?: number
videoBytes?: number
editWindowMinutes?: number
editsPerPost?: number
postsPerHour?: number
followsPerDay?: number
maxFollowing?: number
fanoutMaxFollowers?: number
fanoutBatch?: number
feedDays?: number
storyHours?: number
notificationDays?: number
ranking?: {
follows: number
sharesServer: number
affinity: number
affinityCap: number
engagement: number
likedByFollowee: number
popular: number
notInterestedAuthor: number
notInterestedTag: number
halfLifeHours: number
}
blockedWords?: string[]
}
settings: {
postLength: number
mediaPerPost: number
imageBytes: number
videoBytes: number
editWindowMinutes: number
editsPerPost: number
postsPerHour: number
followsPerDay: number
maxFollowing: number
fanoutMaxFollowers: number
fanoutBatch: number
feedDays: number
storyHours: number
notificationDays: number
ranking: {
follows: number
sharesServer: number
affinity: number
affinityCap: number
engagement: number
likedByFollowee: number
popular: number
notInterestedAuthor: number
notInterestedTag: number
halfLifeHours: number
}
blockedWords: string[]
}
defaults: {
postLength: number
mediaPerPost: number
imageBytes: number
videoBytes: number
editWindowMinutes: number
editsPerPost: number
postsPerHour: number
followsPerDay: number
maxFollowing: number
fanoutMaxFollowers: number
fanoutBatch: number
feedDays: number
storyHours: number
notificationDays: number
ranking: {
follows: number
sharesServer: number
affinity: number
affinityCap: number
engagement: number
likedByFollowee: number
popular: number
notInterestedAuthor: number
notInterestedTag: number
halfLifeHours: number
}
blockedWords: string[]
}
updatedAt: null | number
updatedBy: null | string
}PUT /v1/platform/mirage/social/age/:userId
Records an age check by hand (appeals, testing): outcome 16_plus or under_16 and an optional note. Under 16 deactivates their Feed (hidden from everyone, kept) until a later check says 16 or over. Audited. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:userId | A member's user id (usr_…). |
Response 200
{
ageAssurance: {
outcome: "16_plus" | "under_16"
adult?: boolean
method: "document" | "dev" | "self_declared" | "estimation" | "digital_id" | "bank" | "inference" | "staff"
provider: string
reference?: string
checkedAt: number
recheckAfter?: number
}
}Errors
| Status | Message |
|---|---|
400 | outcome is 16_plus or under_16. |
403 | Age checks are recorded by people. |
DELETE /v1/platform/mirage/social/posts/:postId
Removes a post (reason: one of the report reasons, or a few words); its author is told why. Audited. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:postId | A Feed post id (pst_…). |
| Query parameter | Type | Required | Notes |
|---|---|---|---|
reason | string | No |
Response 204 with no body.
Errors
| Status | Message |
|---|---|
403 | Posts are removed by people. |
GET /v1/platform/mirage/gifs
Mirage's GIF settings: the provider (klipy, giphy or off) and content rating in force, the defaults, and which providers have their key (never the key). Platform staff (platform:admin).
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
settings: {
provider: "off" | "klipy" | "giphy"
rating: "r" | "g" | "pg" | "pg-13"
}
defaults: {
provider: "off" | "klipy" | "giphy"
rating: "r" | "g" | "pg" | "pg-13"
}
keys: {
klipy: boolean
giphy: boolean
}
updatedAt: null | number
updatedBy: null | string
}PUT /v1/platform/mirage/gifs
Changes the GIF provider or content rating (g, pg, pg-13, r). Applies within a minute. Audited. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
settings: {
provider: "off" | "klipy" | "giphy"
rating: "r" | "g" | "pg" | "pg-13"
}
defaults: {
provider: "off" | "klipy" | "giphy"
rating: "r" | "g" | "pg" | "pg-13"
}
keys: {
klipy: boolean
giphy: boolean
}
updatedAt: null | number
updatedBy: null | string
}GET /v1/platform/age/settings
The age gate's settings: mode (off, self_declared: the date of birth, estimation: a face check in ID) and the face check's rules (region, minConfidence, passLowAtLeast, underHighBelow, retries, attemptsPerDay): what's stored, what's in force, the defaults and who changed them last. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
stored: {
mode?: "off" | "self_declared" | "estimation"
estimation?: {
region?: string
minConfidence?: number
passLowAtLeast?: number
underHighBelow?: number
retries?: number
attemptsPerDay?: number
}
}
settings: {
mode: "off" | "self_declared" | "estimation"
estimation: {
region: string
minConfidence: number
passLowAtLeast: number
underHighBelow: number
retries: number
attemptsPerDay: number
}
}
defaults: {
mode: "off" | "self_declared" | "estimation"
estimation: {
region: string
minConfidence: number
passLowAtLeast: number
underHighBelow: number
retries: number
attemptsPerDay: number
}
}
updatedAt: null | number
updatedBy: null | string
}Errors
| Status | Message |
|---|---|
403 | Age checks are changed by people. |
PUT /v1/platform/age/settings
Changes the age gate's mode or the face check's rules (estimation, any of them; null puts one back to its default). Takes effect within a minute. Audited with the mode. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
previousMode: "off" | "self_declared" | "estimation"
stored: {
mode?: "off" | "self_declared" | "estimation"
estimation?: {
region?: string
minConfidence?: number
passLowAtLeast?: number
underHighBelow?: number
retries?: number
attemptsPerDay?: number
}
}
settings: {
mode: "off" | "self_declared" | "estimation"
estimation: {
region: string
minConfidence: number
passLowAtLeast: number
underHighBelow: number
retries: number
attemptsPerDay: number
}
}
defaults: {
mode: "off" | "self_declared" | "estimation"
estimation: {
region: string
minConfidence: number
passLowAtLeast: number
underHighBelow: number
retries: number
attemptsPerDay: number
}
}
updatedAt: null | number
updatedBy: null | string
}Errors
| Status | Message |
|---|---|
403 | Age checks are changed by people. |
GET /v1/platform/age/accounts
An account's age by email: its date of birth (and whether staff set it), the age it gives or its last check's, the gate and face checks left today. 404 when nobody has the address. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
email | string | No | "" |
Response 200
{
account: {
userId: string
email: string
name: null | string
createdAt?: number
}
age: {
birthDate: null | string
birthDateSource: null | "staff" | "self"
age: null | {
outcome: "16_plus" | "under_16"
adult?: boolean
method: "document" | "dev" | "self_declared" | "estimation" | "digital_id" | "bank" | "inference" | "staff"
provider: string
reference?: string
checkedAt: number
recheckAfter?: number
}
gate: {
open: true
method: "none" | "document" | "dev" | "self_declared" | "estimation" | "digital_id" | "bank" | "inference" | "staff"
checkedAt: number
} | {
open: false
reason: "under_16" | "unchecked"
recheckAfter: null | number
check?: "estimation" | "date_of_birth"
}
mode: "off" | "self_declared" | "estimation"
estimation: {
available: boolean
standIn: boolean
attemptsLeft: number
inconclusive: number
retries: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Age checks are changed by people. |
404 | Nobody has that email address. |
PUT /v1/platform/age/accounts/:userId/date-of-birth
Corrects someone's date of birth (dateOfBirth, YYYY-MM-DD; an optional note), with their evidence. The correction becomes the account's age: earlier checks and face-check counts go. Audited with the note (what staff saw), never the date. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:userId | A member's user id (usr_…). |
Response 200
{
age: {
birthDate: null | string
birthDateSource: null | "staff" | "self"
age: null | {
outcome: "16_plus" | "under_16"
adult?: boolean
method: "document" | "dev" | "self_declared" | "estimation" | "digital_id" | "bank" | "inference" | "staff"
provider: string
reference?: string
checkedAt: number
recheckAfter?: number
}
gate: {
open: true
method: "none" | "document" | "dev" | "self_declared" | "estimation" | "digital_id" | "bank" | "inference" | "staff"
checkedAt: number
} | {
open: false
reason: "under_16" | "unchecked"
recheckAfter: null | number
check?: "estimation" | "date_of_birth"
}
mode: "off" | "self_declared" | "estimation"
estimation: {
available: boolean
standIn: boolean
attemptsLeft: number
inconclusive: number
retries: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Age checks are changed by people. |
DELETE /v1/platform/age/accounts/:userId
Clears someone's age (date of birth, checks and face-check counts), so they declare again. Audited. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:userId | A member's user id (usr_…). |
Response 200
{
age: {
birthDate: null | string
birthDateSource: null | "staff" | "self"
age: null | {
outcome: "16_plus" | "under_16"
adult?: boolean
method: "document" | "dev" | "self_declared" | "estimation" | "digital_id" | "bank" | "inference" | "staff"
provider: string
reference?: string
checkedAt: number
recheckAfter?: number
}
gate: {
open: true
method: "none" | "document" | "dev" | "self_declared" | "estimation" | "digital_id" | "bank" | "inference" | "staff"
checkedAt: number
} | {
open: false
reason: "under_16" | "unchecked"
recheckAfter: null | number
check?: "estimation" | "date_of_birth"
}
mode: "off" | "self_declared" | "estimation"
estimation: {
available: boolean
standIn: boolean
attemptsLeft: number
inconclusive: number
retries: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Age checks are changed by people. |
GET /v1/platform/health/metrics
Every Health metric definition (enabled or not): category, kind, units, aggregation, sensitivity, HealthKit mapping and leaderboard mode.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
metrics: {
metricId: string
label: string
category: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
kind: "duration" | "event" | "category" | "quantity"
unit: string
units: {
id: string
label: string
factor: number
offset?: number
}[]
aggregation: "duration" | "count" | "sum" | "average" | "latest"
decimals: number
sensitivity: "standard" | "sensitive" | "sexual"
values?: {
value: number
label: string
}[]
healthKit?: {
identifier: string
kind: "category" | "quantity" | "workout"
unit?: string
readOnly?: boolean
factor?: number
categoryValues?: number[]
}
healthConnect?: string
leaderboard?: "count" | "sum" | "average"
min?: number
max?: number
pinned?: boolean
description?: string
enabled: boolean
sortOrder: number
}[]
}PUT /v1/platform/health/metrics/:metricId
Adds or changes a metric definition. In the platform's audit log.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
Response 200
{
metric: {
metricId: string
label: string
category: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
kind: "duration" | "event" | "category" | "quantity"
unit: string
units: {
id: string
label: string
factor: number
offset?: number
}[]
aggregation: "duration" | "count" | "sum" | "average" | "latest"
decimals: number
sensitivity: "standard" | "sensitive" | "sexual"
values?: {
value: number
label: string
}[]
healthKit?: {
identifier: string
kind: "category" | "quantity" | "workout"
unit?: string
readOnly?: boolean
factor?: number
categoryValues?: number[]
}
healthConnect?: string
leaderboard?: "count" | "sum" | "average"
min?: number
max?: number
pinned?: boolean
description?: string
enabled: boolean
sortOrder: number
}
}PUT /v1/platform/health/metrics/:metricId/enabled
Turns a metric on or off (enabled); definitions are never deleted. In the platform's audit log.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
enabled | boolean | Yes |
Also checked: Unknown fields are rejected.
Response 200
{
metric: {
enabled: boolean
metricId: string
label: string
category: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
kind: "duration" | "event" | "category" | "quantity"
unit: string
units: {
id: string
label: string
factor: number
offset?: number
}[]
aggregation: "duration" | "count" | "sum" | "average" | "latest"
decimals: number
sensitivity: "standard" | "sensitive" | "sexual"
values?: {
value: number
label: string
}[]
healthKit?: {
identifier: string
kind: "category" | "quantity" | "workout"
unit?: string
readOnly?: boolean
factor?: number
categoryValues?: number[]
}
healthConnect?: string
leaderboard?: "count" | "sum" | "average"
min?: number
max?: number
pinned?: boolean
description?: string
sortOrder: number
}
}POST /v1/platform/health/metrics/seed
Adds seed definitions the registry is missing (never changes existing ones).
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
added: string[]
}GET /v1/platform/health/settings
Health's platform settings (samples per request, how long the audit keeps entries) and their defaults.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
settings: {
samplesPerRequest: number
auditDays: number
}
defaults: {
samplesPerRequest: number
auditDays: number
}
updatedAt: null | number
updatedBy: null | string
}PUT /v1/platform/health/settings
Changes Health's platform settings. In the platform's audit log.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
settings: {
samplesPerRequest: number
auditDays: number
}
defaults: {
samplesPerRequest: number
auditDays: number
}
updatedAt: null | number
updatedBy: null | string
}GET /v1/platform/profile-effects
Every profile effect in the catalogue (enabled or not): id, name, renderer (sparkles, snow, hearts, glow, aurora or confetti), colors and order.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
effects: {
effectId: string
name: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
enabled: boolean
sortOrder: number
}[]
}PUT /v1/platform/profile-effects/:effectId
Adds or changes a profile effect (name, renderer, one to four colors, enabled, sortOrder). People wearing it keep the look they chose until they choose again. In the platform's audit log.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:effectId | A profile effect's id from the catalogue (starlight, northern-lights): 2–40 lowercase letters, digits or hyphens. |
Response 200
{
effect: {
effectId: string
name: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
enabled: boolean
sortOrder: number
}
}PUT /v1/platform/profile-effects/:effectId/enabled
Turns a profile effect on or off (enabled): off, nobody can choose it. In the platform's audit log.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:effectId | A profile effect's id from the catalogue (starlight, northern-lights): 2–40 lowercase letters, digits or hyphens. |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
enabled | boolean | Yes |
Also checked: Unknown fields are rejected.
Response 200
{
effect: {
effectId: string
name: string
renderer: "sparkles" | "snow" | "hearts" | "glow" | "aurora" | "confetti"
colors: string[]
enabled: boolean
sortOrder: number
}
}POST /v1/platform/profile-effects/seed
Adds the built-in profile effects the catalogue is missing (never changes existing ones).
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
added: string[]
}GET /v1/platform/locales
The languages people can choose, as data (locales: locale, label, spelling (au or us), weekStart (0 Sunday … 6 Saturday), enabled, sortOrder), whether a list is stored or the built-in one applies, the built-in one, and who changed it last. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
locales: {
locale: string
label: string
spelling: "au" | "us"
weekStart: number
enabled: boolean
sortOrder: number
}[]
stored: boolean
defaults: {
locale: string
label: string
spelling: "au" | "us"
weekStart: number
enabled: boolean
sortOrder: number
}[]
updatedAt: null | number
updatedBy: null | string
}PUT /v1/platform/locales
Replaces the list of languages (locales: the whole list; null goes back to the built-in English (Australia) and English (US)). en-AU stays enabled; each language once. People's apps see the change within a minute. In the platform's audit log.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
locales: {
locale: string
label: string
spelling: "au" | "us"
weekStart: number
enabled: boolean
sortOrder: number
}[]
stored: boolean
defaults: {
locale: string
label: string
spelling: "au" | "us"
weekStart: number
enabled: boolean
sortOrder: number
}[]
updatedAt: null | number
updatedBy: null | string
}Errors
| Status | Message |
|---|---|
400 | Send locales: the list, or null for the built-in one. |
GET /v1/platform/badges
The settings, the seed (to reset to) and who saved them last.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
settings: {
tiers: {
tierId: string
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
}[]
earlySupporterCutoff: string
gapDays: number
}
defaults: {
tiers: {
tierId: string
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
}[]
earlySupporterCutoff: string
gapDays: number
}
updatedAt: null | number
updatedBy: null | string
}PUT /v1/platform/badges
Saves the tiers, the cutoff (YYYY-MM-DD) and the lapse allowed in days.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
settings: {
tiers: {
tierId: string
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
}[]
earlySupporterCutoff: string
gapDays: number
}
defaults: {
tiers: {
tierId: string
name: string
months: number
art: "quartz" | "bronze" | "silver" | "gold" | "platinum" | "diamond" | "emerald" | "ruby" | "opal" | "early"
form: "stone" | "cut" | "brilliant" | "radiant" | "spark"
}[]
earlySupporterCutoff: string
gapDays: number
}
updatedAt: null | number
updatedBy: null | string
}GET /v1/platform/badges/people/:who
One person (usr_… or their email): their badges, whether they show them, staff's grant and the accounts that count.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:who | A person's user id (usr_…) or an email address, as listed in the request's participants. |
Response 200
{
person: {
userId: string
name: null | string
email: string
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
}
}[]
hidden: boolean
grant: null | {
state: "revoked" | "granted"
by: string
at: number
}
accounts: {
ownerId: string
ownerType: "user" | "org"
status: "active" | "none" | "canceled" | "incomplete" | "trialing" | "past_due" | "unpaid"
subscribedAt?: number
firstPaidAt?: number
paidSince?: number
lapsedAt?: number
}[]
}
}PUT /v1/platform/badges/people/:who/early-supporter
Early Supporter by hand: granted, revoked, or auto (billing decides again).
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:who | A person's user id (usr_…) or an email address, as listed in the request's participants. |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
state | "granted" | "revoked" | "auto" | Yes |
Also checked: Unknown fields are rejected.
Response 200
{
person: {
userId: string
name: null | string
email: string
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
}
}[]
hidden: boolean
grant: null | {
state: "revoked" | "granted"
by: string
at: number
}
accounts: {
ownerId: string
ownerType: "user" | "org"
status: "active" | "none" | "canceled" | "incomplete" | "trialing" | "past_due" | "unpaid"
subscribedAt?: number
firstPaidAt?: number
paidSince?: number
lapsedAt?: number
}[]
}
}GET /v1/platform/people/subsidised
Everyone the platform pays a personal plan for, newest first (an ended one until the reconcile removes it).
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
people: {
userId: string
name: null | string
email: string
plan: {
planId?: string
via: "subsidised" | "subscription" | "free"
planName?: string
}
subsidy: null | {
planId: string
planName?: string
by: string
byLabel?: string
at: number
until?: number
note?: string
active: boolean
}
subscription: null | {
planId?: string
planName?: string
status: "active" | "none" | "canceled" | "incomplete" | "trialing" | "past_due" | "unpaid"
interval?: "month" | "two-year" | "year"
}
}[]
}GET /v1/platform/people/:who
One person (usr_… or their email): the plan they have, a subsidy and their own subscription.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:who | A person's user id (usr_…) or an email address, as listed in the request's participants. |
Response 200
{
person: {
userId: string
name: null | string
email: string
plan: {
planId?: string
via: "subsidised" | "subscription" | "free"
planName?: string
}
subsidy: null | {
planId: string
planName?: string
by: string
byLabel?: string
at: number
until?: number
note?: string
active: boolean
}
subscription: null | {
planId?: string
planName?: string
status: "active" | "none" | "canceled" | "incomplete" | "trialing" | "past_due" | "unpaid"
interval?: "month" | "two-year" | "year"
}
}
}PUT /v1/platform/people/:who/subsidy
Gives the person a personal plan the platform pays for, or changes its plan, end (until, ms; null: no end) or note (null clears it). No card and no subscription; one they pay for keeps running and they get the higher of the two.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:who | A person's user id (usr_…) or an email address, as listed in the request's participants. |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
planId | string | Yes | matches ^[a-z0-9][a-z0-9-]{1,62}$ |
until | integer | No | > 0; can be null |
note | string | No | up to 500 characters; can be null |
Also checked: Unknown fields are rejected.
Response 200
{
person: {
userId: string
name: null | string
email: string
plan: {
planId?: string
via: "subsidised" | "subscription" | "free"
planName?: string
}
subsidy: null | {
planId: string
planName?: string
by: string
byLabel?: string
at: number
until?: number
note?: string
active: boolean
}
subscription: null | {
planId?: string
planName?: string
status: "active" | "none" | "canceled" | "incomplete" | "trialing" | "past_due" | "unpaid"
interval?: "month" | "two-year" | "year"
}
}
}DELETE /v1/platform/people/:who/subsidy
Takes the subsidy away: back to their paid plan, else Free (files kept; the retention period starts).
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:who | A person's user id (usr_…) or an email address, as listed in the request's participants. |
Response 200
{
person: {
userId: string
name: null | string
email: string
plan: {
planId?: string
via: "subsidised" | "subscription" | "free"
planName?: string
}
subsidy: null | {
planId: string
planName?: string
by: string
byLabel?: string
at: number
until?: number
note?: string
active: boolean
}
subscription: null | {
planId?: string
planName?: string
status: "active" | "none" | "canceled" | "incomplete" | "trialing" | "past_due" | "unpaid"
interval?: "month" | "two-year" | "year"
}
}
}GET /v1/platform/mirage/reports
Every Mirage report (open ones, or status=all), newest first, with the reporter and server: the platform's review queue. before pages. platform:admin.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Query parameter | Type | Required | Notes |
|---|---|---|---|
status | string | No | |
before | string | No |
Response 200
{
reports: {
reporter?: {
userId: string
name: string
}
serverName?: null | string
reportId: string
kind: "post" | "message" | "user" | "server" | "dm_message" | "story"
status: "open" | "actioned" | "dismissed"
reason: string
reasonLabel: string
details: null | string
target: {
userId: string
name: string
}
serverId: null | string
channelId: null | string
messageId: null | string
dmId: null | string
snapshot: unknown
resolvedBy: null | {
userId: string
name: string
}
resolvedAt: null | number
note: null | string
createdAt: number
}[]
next: null | string
}PATCH /v1/platform/mirage/reports/:reportId
Marks any report actioned, dismissed or open, with an optional note. platform:admin.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:reportId | A report's id: a Mirage report's (mrp_…) or a feedback report's (fbk_…). |
Response 200
{
report: {
reporter?: {
userId: string
name: string
}
serverName?: null | string
reportId: string
kind: "post" | "message" | "user" | "server" | "dm_message" | "story"
status: "open" | "actioned" | "dismissed"
reason: string
reasonLabel: string
details: null | string
target: {
userId: string
name: string
}
serverId: null | string
channelId: null | string
messageId: null | string
dmId: null | string
snapshot: unknown
resolvedBy: null | {
userId: string
name: string
}
resolvedAt: null | number
note: null | string
createdAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Reports are reviewed by people. |
GET /v1/platform/pipelines/runners
Every hosted runner type (enabled or not): label, CodeBuild project, environment and compute type, image, minute multiplier, longest timeout, Docker, orgs it's limited to. Needs platform:admin.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
runners: {
label: string
description: string
project: "lambda" | "container"
environmentType: string
computeType: string
image: string
multiplier: number
maxMinutes: number
docker: boolean
orgs: string[]
isDefault: boolean
enabled: boolean
sortOrder: number
}[]
}Errors
| Status | Message |
|---|---|
503 | No platform table |
PUT /v1/platform/pipelines/runners/:label
Creates or changes a hosted runner type (Lambda compute: at most 15 minutes, no Docker). Needs platform:infra.
Auth: user access token or platform agent key · Scope: platform:infra in the platform organization
| Path parameter | Description |
|---|---|
:label | Hosted runner type label, e.g. linux-arm64. |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
description | string | No | "" | up to 200 characters; trimmed |
project | "lambda" | "container" | Yes | ||
environmentType | "ARM_LAMBDA_CONTAINER" | "LINUX_LAMBDA_CONTAINER" | "ARM_CONTAINER" | "LINUX_CONTAINER" | Yes | ||
computeType | string | Yes | matches ^BUILD_(LAMBDA_\d+GB|GENERAL1_[A-Z0-9]+)$ | |
image | string | Yes | 3–300 characters; trimmed | |
multiplier | number | No | 1 | 0–100 |
maxMinutes | integer | Yes | 1–2160 | |
docker | boolean | No | false | |
orgs | string[] | No | [] | up to 100 items; each matches ^org_[0-9a-z]+$ |
isDefault | boolean | No | false | |
enabled | boolean | No | true | |
sortOrder | integer | No | 0 |
Response 200
{
runner: {
label: string
createdAt?: number
updatedAt?: number
project: "lambda" | "container"
description?: string
image: string
enabled?: boolean
orgs?: string[]
isDefault?: boolean
sortOrder?: number
environmentType: string
computeType: string
multiplier?: number
maxMinutes: number
docker?: boolean
}
}Errors
| Status | Message |
|---|---|
400 | Labels are lower case letters, numbers, '.' and '-' (not hosted, self-hosted or platform-deploy) |
400 | Lambda compute: a Lambda environment type, at most 15 minutes, no Docker |
400 | Container runners use ARM_CONTAINER or LINUX_CONTAINER |
503 | No platform table |
DELETE /v1/platform/pipelines/runners/:label
Removes a hosted runner type; jobs naming it fail with the list of others. Needs platform:infra.
Auth: user access token or platform agent key · Scope: platform:infra in the platform organization
| Path parameter | Description |
|---|---|
:label | Hosted runner type label, e.g. linux-arm64. |
Response 204 with no body.
Errors
| Status | Message |
|---|---|
503 | No platform table |
GET /v1/platform/pipelines/settings
The platform's pipeline settings (matrix and run size, log, artifact and cache sizes, workflow file size, schedule interval, approval expiry, queue timeout, secret size and count) and their defaults. Needs platform:admin.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
settings: {
maxMatrixJobs: number
maxJobs: number
maxLogMb: number
maxArtifactMb: number
maxCacheMb: number
maxWorkflowKb: number
scheduleMinMinutes: number
approvalDays: number
queueHours: number
heartbeatMinutes: number
defaultArtifactDays: number
cacheUnusedDays: number
secretKb: number
maxSecrets: number
}
defaults: {
maxMatrixJobs: number
maxJobs: number
maxLogMb: number
maxArtifactMb: number
maxCacheMb: number
maxWorkflowKb: number
scheduleMinMinutes: number
approvalDays: number
queueHours: number
heartbeatMinutes: number
defaultArtifactDays: number
cacheUnusedDays: number
secretKb: number
maxSecrets: number
}
}Errors
| Status | Message |
|---|---|
503 | No platform table |
PUT /v1/platform/pipelines/settings
Changes the pipeline settings; they apply within a minute. Needs platform:infra.
Auth: user access token or platform agent key · Scope: platform:infra in the platform organization
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
maxMatrixJobs | integer | Yes | 1–1000 |
maxJobs | integer | Yes | 1–1000 |
maxLogMb | integer | Yes | 1–1024 |
maxArtifactMb | integer | Yes | 1–5120 |
maxCacheMb | integer | Yes | 1–5120 |
maxWorkflowKb | integer | Yes | 16–4096 |
scheduleMinMinutes | integer | Yes | 1–1440 |
approvalDays | integer | Yes | 1–90 |
queueHours | integer | Yes | 1–168 |
heartbeatMinutes | integer | Yes | 1–60 |
defaultArtifactDays | integer | Yes | 1–400 |
cacheUnusedDays | integer | Yes | 1–90 |
secretKb | integer | Yes | 1–64 |
maxSecrets | integer | Yes | 1–1000 |
Response 200
{
settings: {
maxMatrixJobs: number
maxJobs: number
maxLogMb: number
maxArtifactMb: number
maxCacheMb: number
maxWorkflowKb: number
scheduleMinMinutes: number
approvalDays: number
queueHours: number
heartbeatMinutes: number
defaultArtifactDays: number
cacheUnusedDays: number
secretKb: number
maxSecrets: number
}
}Errors
| Status | Message |
|---|---|
503 | No platform table |
GET /v1/platform/feedback
The inbox: reports newest first (open ones unless status says otherwise), filtered, with possible duplicates.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
reports: {
reportId: string
type: "bug" | "feedback" | "suggestion"
typeLabel: string
status: "pending" | "resolved" | "dismissed" | "spam" | "new" | "triaged"
severity: null | "medium" | "low" | "high" | "critical"
title: string
description: string
app: string
appLabel: string
reporter: {
userId: string
name: null | string
email: null | string
}
contactOk: boolean
orgId: null | string
context: {
app?: string
appName?: string
version?: string
build?: string
path?: string
orgId?: string
browser?: string
os?: string
device?: string
viewport?: {
width: number
height: number
scale?: number
}
locale?: string
timeZone?: string
shell?: string
errors?: {
message: string
at: number
source?: string
}[]
requests?: {
method: string
url: string
status: number
at: number
}[]
routes?: {
path: string
at: number
}[]
}
images: {
imageId: string
type: "image/jpeg" | "image/png" | "image/webp"
size: number
}[]
fingerprint: string
similar: {
count: number
reportIds: string[]
}
spamReason: null | string
commentCount: number
issue: null | {
orgId: string
issueId: string
key: string
spaceKey: string
}
updatedBy: null | string
createdAt: number
submittedAt: number
updatedAt?: number
}[]
next: null | string
counts: {
resolved: number
dismissed: number
spam: number
new: number
triaged: number
}
apps: {
app: string
label: string
}[]
}Errors
| Status | Message |
|---|---|
400 | Unknown filter. |
403 | Feedback is reviewed by people. |
GET /v1/platform/feedback/:reportId
One report: images (signed links), comments and possible duplicates.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:reportId | A report's id: a Mirage report's (mrp_…) or a feedback report's (fbk_…). |
Response 200
{
report: {
images: {
url: null | string
imageId: string
type: "image/jpeg" | "image/png" | "image/webp"
size: number
}[]
reportId: string
type: "bug" | "feedback" | "suggestion"
typeLabel: string
status: "pending" | "resolved" | "dismissed" | "spam" | "new" | "triaged"
severity: null | "medium" | "low" | "high" | "critical"
title: string
description: string
app: string
appLabel: string
reporter: {
userId: string
name: null | string
email: null | string
}
contactOk: boolean
orgId: null | string
context: {
app?: string
appName?: string
version?: string
build?: string
path?: string
orgId?: string
browser?: string
os?: string
device?: string
viewport?: {
width: number
height: number
scale?: number
}
locale?: string
timeZone?: string
shell?: string
errors?: {
message: string
at: number
source?: string
}[]
requests?: {
method: string
url: string
status: number
at: number
}[]
routes?: {
path: string
at: number
}[]
}
fingerprint: string
similar: {
count: number
reportIds: string[]
}
spamReason: null | string
commentCount: number
issue: null | {
orgId: string
issueId: string
key: string
spaceKey: string
}
updatedBy: null | string
createdAt: number
submittedAt: number
updatedAt?: number
}
comments: {
commentId: string
author: {
userId: string
name: null | string
}
body: string
createdAt?: number
}[]
similar: {
reportId: string
title: string
type: "bug" | "feedback" | "suggestion"
status: "pending" | "resolved" | "dismissed" | "spam" | "new" | "triaged"
appLabel: string
createdAt?: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Feedback is reviewed by people. |
PATCH /v1/platform/feedback/:reportId
Triage: status, severity (bugs) or type.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:reportId | A report's id: a Mirage report's (mrp_…) or a feedback report's (fbk_…). |
Response 200
{
report: {
reportId: string
type: "bug" | "feedback" | "suggestion"
typeLabel: string
status: "pending" | "resolved" | "dismissed" | "spam" | "new" | "triaged"
severity: null | "medium" | "low" | "high" | "critical"
title: string
description: string
app: string
appLabel: string
reporter: {
userId: string
name: null | string
email: null | string
}
contactOk: boolean
orgId: null | string
context: {
app?: string
appName?: string
version?: string
build?: string
path?: string
orgId?: string
browser?: string
os?: string
device?: string
viewport?: {
width: number
height: number
scale?: number
}
locale?: string
timeZone?: string
shell?: string
errors?: {
message: string
at: number
source?: string
}[]
requests?: {
method: string
url: string
status: number
at: number
}[]
routes?: {
path: string
at: number
}[]
}
images: {
imageId: string
type: "image/jpeg" | "image/png" | "image/webp"
size: number
}[]
fingerprint: string
similar: {
count: number
reportIds: string[]
}
spamReason: null | string
commentCount: number
issue: null | {
orgId: string
issueId: string
key: string
spaceKey: string
}
updatedBy: null | string
createdAt: number
submittedAt: number
updatedAt?: number
}
}Errors
| Status | Message |
|---|---|
403 | Feedback is reviewed by people. |
POST /v1/platform/feedback/:reportId/comments
An admin's comment (only admins see them).
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:reportId | A report's id: a Mirage report's (mrp_…) or a feedback report's (fbk_…). |
Response 201
{
comment: {
commentId: string
author: {
userId: string
name: null | string
}
body: string
createdAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Feedback is reviewed by people. |
POST /v1/platform/feedback/:reportId/convert
Turns a report into an issue in one of the platform org's Tasks spaces (space: its key or id), with its text, context and images; the report links to the issue and is triaged.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:reportId | A report's id: a Mirage report's (mrp_…) or a feedback report's (fbk_…). |
Response 200
{
issue: {
orgId: string
issueId: string
key: string
spaceKey: string
}
report: {
reportId: string
type: "bug" | "feedback" | "suggestion"
typeLabel: string
status: "pending" | "resolved" | "dismissed" | "spam" | "new" | "triaged"
severity: null | "medium" | "low" | "high" | "critical"
title: string
description: string
app: string
appLabel: string
reporter: {
userId: string
name: null | string
email: null | string
}
contactOk: boolean
orgId: null | string
context: {
app?: string
appName?: string
version?: string
build?: string
path?: string
orgId?: string
browser?: string
os?: string
device?: string
viewport?: {
width: number
height: number
scale?: number
}
locale?: string
timeZone?: string
shell?: string
errors?: {
message: string
at: number
source?: string
}[]
requests?: {
method: string
url: string
status: number
at: number
}[]
routes?: {
path: string
at: number
}[]
}
images: {
imageId: string
type: "image/jpeg" | "image/png" | "image/webp"
size: number
}[]
fingerprint: string
similar: {
count: number
reportIds: string[]
}
spamReason: null | string
commentCount: number
issue: null | {
orgId: string
issueId: string
key: string
spaceKey: string
}
updatedBy: null | string
createdAt: number
submittedAt: number
updatedAt?: number
}
}Errors
| Status | Message |
|---|---|
400 | Choose a space. |
403 | Feedback is reviewed by people. |
403 | Missing scope platform:admin |
DELETE /v1/platform/feedback/:reportId
Deletes a report, its comments and its images.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:reportId | A report's id: a Mirage report's (mrp_…) or a feedback report's (fbk_…). |
Response 200
{
reportId: string
}Errors
| Status | Message |
|---|---|
403 | Feedback is reviewed by people. |
GET /v1/platform/orgs
Platform organizations and the newest customer organizations.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
orgs: {
name: string
createdAt?: number
updatedAt?: number
orgId: string
slug: string
kind: "platform" | "customer"
planId?: string
subsidised?: boolean
billingRequired?: boolean
pending?: boolean
auditExportBucketId?: string
logoUri?: string
contactEmail?: string
authStrength?: "any" | "mfa" | "passkey"
authMethods?: string[]
authGraceUntil?: number
sessionHours?: number
}[]
}POST /v1/platform/orgs
Onboards a customer: the org (slug claimed atomically, optional plan) and an owner invite. The invite link is in the response once (and mailed where SES is set up); the owner joins through ID.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
name | string | Yes | 1–64 characters; trimmed | |
slug | string | Yes | matches ^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){1,38}$; trimmed | |
ownerEmail | string | Yes | up to 254 characters; email address | |
planId | string | No | matches ^[a-z0-9][a-z0-9-]{1,62}$; can be null | |
subsidised | boolean | No | false |
Response 201
{
org: {
name: string
createdAt?: number
updatedAt?: number
orgId: string
slug: string
kind: "platform" | "customer"
planId?: string
subsidised?: boolean
billingRequired?: boolean
pending?: boolean
auditExportBucketId?: string
logoUri?: string
contactEmail?: string
authStrength?: "any" | "mfa" | "passkey"
authMethods?: string[]
authGraceUntil?: number
sessionHours?: number
}
invite: {
inviteId: string
email: string
link: string
delivered: boolean
expiresAt: number
}
}Errors
| Status | Message |
|---|---|
400 | Unknown plan |
403 | Onboarding needs a signed-in admin. |
PATCH /v1/platform/orgs/:orgId
Assigns an organization to a plan (planId; null returns it to the default plan) and/or sets whether the platform pays for it (subsidised: every app, no card). Ending a subsidy makes the organization subscribe in Tenant → Billing before its apps open again.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:orgId | Organization id (org_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
planId | string | No | matches ^[a-z0-9][a-z0-9-]{1,62}$; can be null |
subsidised | boolean | No |
Response 200
{
org: {
name: string
createdAt?: number
updatedAt?: number
orgId: string
slug: string
kind: "platform" | "customer"
planId?: string
subsidised?: boolean
billingRequired?: boolean
pending?: boolean
auditExportBucketId?: string
logoUri?: string
contactEmail?: string
authStrength?: "any" | "mfa" | "passkey"
authMethods?: string[]
authGraceUntil?: number
sessionHours?: number
}
}Errors
| Status | Message |
|---|---|
400 | Unknown plan |
404 | Organization not found |
GET /v1/platform/stats
Counts of organizations, customers, regions and locations.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
orgs: number
customers: number
regions: number
locations: number
}GET /v1/platform/audit
The platform organization's audit log: registry and plan changes, onboarding and the platform organization's own activity.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
cursor | string | No | up to 4,096 characters | |
action | string | No | matches ^[a-z_]{1,32}(\.[a-z_]{1,32})?$ | |
actor | string | No | 1–512 characters | |
target | string | No | matches ^[a-z_]{1,32}:[^\s]{1,512}$ | |
limit | integer | No | 50 | 1–100; coerced from a string |
Response 200
{
events: {
eventId: string
orgId: string
action: string
actor: {
type: "user" | "key" | "device" | "system"
id: string
label?: string
}
target: {
type: string
id: string
label?: string
}
metadata?: {
[key: string]: unknown
}
ip?: string
userAgent?: string
createdAt: number
}[]
cursor: null | string
retentionDays?: number
}Errors
| Status | Message |
|---|---|
400 | Invalid cursor |
403 | Missing scope platform:admin |
GET /v1/platform/oauth-clients
Self-registered (MCP) clients at ID: who registered what, how many people connected, last use.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
clients: {
connections: number
createdAt?: number
lastUsedAt?: number
expiresAt?: number
logoUri?: string
clientUri?: string
clientId: string
name: string
redirectUris: string[]
}[]
}DELETE /v1/platform/oauth-clients/:clientId
Removes a self-registered client: its approvals and refresh tokens stop working at once (the token endpoint no longer knows the client); access tokens it holds last up to an hour.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:clientId | Sign-in client id (cli_…). |
Response 204 with no body.
Errors
| Status | Message |
|---|---|
404 | Client not found |
GET /v1/platform/regions
The region and location registry.
Auth: user access token or platform agent key · Scope: platform:infra in the platform organization
Response 200
{
regions: {
name: string
status?: "active" | "disabled"
createdAt?: number
updatedAt?: number
unavailable?: string[]
regionId: string
}[]
locations: {
label: string
status?: "active" | "disabled" | "preview"
createdAt?: number
updatedAt?: number
locationId: string
isDefault?: boolean
regions: string[]
sortOrder?: number
}[]
}PUT /v1/platform/regions/:regionId
Creates or replaces a region. Services that ran from the cached registry see the change within a minute.
Auth: user access token or platform agent key · Scope: platform:infra in the platform organization
| Path parameter | Description |
|---|---|
:regionId | AWS region id, e.g. us-west-1. |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
name | string | Yes | 1–100 characters | |
status | "active" | "disabled" | No | "active" | |
unavailable | string[] | No | [] | up to 100 items; each up to 64 characters |
Response 200
{
region: {
name: string
status?: "active" | "disabled"
createdAt?: number
updatedAt?: number
unavailable?: string[]
regionId: string
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
PUT /v1/platform/locations/:locationId
Creates or replaces a location with its ordered region list (primary first).
Auth: user access token or platform agent key · Scope: platform:infra in the platform organization
| Path parameter | Description |
|---|---|
:locationId | Location id: a platform location, or under Maps an org's alert location (mlc_…). |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
label | string | Yes | 1–100 characters | |
regions | string[] | Yes | 1–10 items; each matches ^[a-z0-9][a-z0-9-]{1,62}$ | |
status | "active" | "preview" | "disabled" | No | "active" | |
isDefault | boolean | No | false | |
sortOrder | integer | No | 0 |
Response 200
{
location: {
label: string
status?: "active" | "disabled" | "preview"
createdAt?: number
updatedAt?: number
locationId: string
isDefault?: boolean
regions: string[]
sortOrder?: number
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
GET /v1/platform/billing/signups
Pay first (docs/billing/README.md → Pay first): the funnel for the last 30 days (checkouts started, paid, details given, onboarding complete) and the sign-ups that paid but haven't finished (the pending orgs and people), newest first.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
funnel: {
day: string
started: number
paid: number
named: number
complete: number
}[]
pending: {
signupId: string
audience: "personal" | "business"
planId: string
email?: string
orgName?: string
orgId?: string
next: "invite" | "done" | "account" | "pay" | "details" | "secure"
paidAt?: number
createdAt: number
remindersSent: number
}[]
}GET /v1/platform/billing/promotions
Promotion codes (docs/billing/promotions.md): the codes and coupons in the stage's payment provider, the subscriptions carrying a discount now, who may create codes, and whether this person may.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
discounted: {
ownerId: string
ownerType: "user" | "org"
ownerName?: string
planId?: string
interval?: "month" | "two-year" | "year"
status: string
subscriptionId?: string
discount: {
percentOff?: number
amountOff?: number
currency?: string
duration: "once" | "repeating" | "forever"
durationInMonths?: number
code?: string
name?: string
startsAt?: number
endsAt?: number
}
}[]
creators: {
roles: ("owner" | "admin" | "developer" | "viewer")[]
emails: string[]
}
canCreate: boolean
mode: "live" | "test" | "local"
codes: {
promotionCodeId: string
code: string
active: boolean
couponId: string
name?: string
discount: {
percentOff?: number
amountOff?: number
currency?: string
duration: "once" | "repeating" | "forever"
durationInMonths?: number
}
usable: boolean
timesRedeemed: number
maxRedemptions?: number
expiresAt?: number
firstTimeOnly: boolean
minimumAmount?: number
planIds?: string[]
intervals?: ("month" | "two-year" | "year")[]
email?: string
ownerId?: string
customerId?: string
createdAt: number
createdBy?: string
}[]
coupons: {
createdAt: number
planIds?: string[]
discount: {
percentOff?: number
amountOff?: number
currency?: string
duration: "once" | "repeating" | "forever"
durationInMonths?: number
}
valid: boolean
timesRedeemed: number
name?: string
couponId: string
}[]
}POST /v1/platform/billing/promotions
Makes a code (and its coupon, unless couponId names one). Who may: billing.promotionCreators.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
code | string | Yes | 3–40 characters; trimmed |
name | string | No | up to 80 characters; trimmed |
couponId | string | No | matches ^[A-Za-z0-9_-]{1,100}$ |
percentOff | integer | No | 1–100 |
amountOff | integer | No | ≥ 1 |
duration | "once" | "repeating" | "forever" | No | |
durationInMonths | integer | No | 1–36 |
maxRedemptions | integer | No | 1–1000000 |
expiresAt | integer | No | > 0 |
planIds | string[] | No | up to 20 items; each matches ^[a-z0-9][a-z0-9-]{1,62}$ |
intervals | ("two-year" | "year" | "month")[] | No | up to 3 items |
email | string | No | up to 254 characters; email address |
ownerId | string | No | matches ^(org|usr)_[A-Za-z0-9]{6,40}$ |
firstTimeOnly | boolean | No | |
minimumAmount | integer | No | ≥ 1 |
Response 201
{
code: {
promotionCodeId: string
code: string
active: boolean
couponId: string
name?: string
discount: {
percentOff?: number
amountOff?: number
currency?: string
duration: "once" | "repeating" | "forever"
durationInMonths?: number
}
usable: boolean
timesRedeemed: number
maxRedemptions?: number
expiresAt?: number
firstTimeOnly: boolean
minimumAmount?: number
planIds?: string[]
intervals?: ("month" | "two-year" | "year")[]
email?: string
ownerId?: string
customerId?: string
createdAt: number
createdBy?: string
}
}Errors
| Status | Message |
|---|---|
403 | You can't create promotion codes. Ask a platform owner (Admin → Promotions). |
PATCH /v1/platform/billing/promotions/:promotionCodeId
Turns a code off (or on again): no new redemptions; discounts already given keep running.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:promotionCodeId | A promotion code's id (the payment provider's, promo_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
active | boolean | Yes |
Response 200
{
code: {
promotionCodeId: string
code: string
active: boolean
couponId: string
name?: string
discount: {
percentOff?: number
amountOff?: number
currency?: string
duration: "once" | "repeating" | "forever"
durationInMonths?: number
}
usable: boolean
timesRedeemed: number
maxRedemptions?: number
expiresAt?: number
firstTimeOnly: boolean
minimumAmount?: number
planIds?: string[]
intervals?: ("month" | "two-year" | "year")[]
email?: string
ownerId?: string
customerId?: string
createdAt: number
createdBy?: string
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
403 | You can't change promotion codes. |
DELETE /v1/platform/billing/coupons/:couponId
Deletes a coupon: none of its codes can be redeemed any more; discounts already given keep running.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:couponId | A coupon's id (the payment provider's). |
Response 200
{
ok: true
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
403 | You can't delete coupons. |
GET /v1/platform/billing/redemptions
Redemptions, newest first: one code's (?promotionCodeId=) or all.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Query parameter | Type | Required | Notes |
|---|---|---|---|
promotionCodeId | string | No |
Response 200
{
redemptions: {
promotionCodeId: string
code: string
ownerId: string
ownerType: "user" | "org"
ownerName?: string
subscriptionId: string
planId?: string
interval?: "month" | "two-year" | "year"
at: number
}[]
}Errors
| Status | Message |
|---|---|
400 | Invalid code id |
PUT /v1/platform/billing/promotion-creators
Who may create codes: platform roles and people. Platform owners only.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
roles | ("owner" | "admin" | "developer" | "viewer")[] | Yes | up to 4 items |
emails | string[] | Yes | up to 100 items; each up to 254 characters, email address |
Response 200
{
creators: {
roles: ("owner" | "admin" | "developer" | "viewer")[]
emails: string[]
}
}Errors
| Status | Message |
|---|---|
403 | Only platform owners can change who creates codes. |
GET /v1/platform/plans
Plans and their settings. Organizations without a plan use the default plan.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
plans: {
features?: string[]
name: string
createdAt?: number
updatedAt?: number
planId: string
apps?: string[]
audience?: "personal" | "business"
tagline?: string
isDefault?: boolean
sortOrder?: number
priceMonthly?: number
priceAnnual?: number
priceTwoYear?: number
offered?: boolean
trialDays?: number
support?: string
highlights?: string[]
caityMessagesPerDay?: number
auditRetentionDays: number
mailSendPerDay?: number
mailMaxMessageMb?: number
mailMaxRecipients?: number
maxConnectors?: number
maxConnectorTools?: number
driveStorageGb?: number
driveStorageGbPerUser?: number
drivePersonalStorageGb?: number
vaultMaxItems?: number
crmMaxRecords?: number
marketingContacts?: number
marketingSendsPerMonth?: number
pipelineMinutes?: number
pipelineOverage?: boolean
pipelineConcurrency?: number
pipelineMaxMinutes?: number
pipelineLogDays?: number
pipelineArtifactDays?: number
pipelineArtifactGb?: number
pipelineCacheGb?: number
keysMax?: number
keyOpsPerMonth?: number
secretsMax?: number
keyMonthlyPriceCents?: number
keyOpsPer10kPriceCents?: number
}[]
}PUT /v1/platform/plans/:planId
Creates or replaces a plan. Optional limits that aren't sent keep their stored value; null clears one (the default applies). Making a plan the default clears the flag on the others.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:planId | Plan id. |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
name | string | Yes | 1–100 characters; trimmed | |
auditRetentionDays | integer | Yes | 1–3650 | |
isDefault | boolean | No | false | |
sortOrder | integer | No | 0 | |
mailSendPerDay | integer | No | 0–1000000; can be null | |
mailMaxMessageMb | integer | No | 1–40; can be null | |
mailMaxRecipients | integer | No | 1–1000; can be null | |
maxConnectors | integer | No | 0–1000; can be null | |
maxConnectorTools | integer | No | 0–1000; can be null | |
driveStorageGb | integer | No | 0–1000000; can be null | |
drivePersonalStorageGb | integer | No | 0–1000000; can be null | |
vaultMaxItems | integer | No | 0–1000000; can be null | |
crmMaxRecords | integer | No | 0–1000000000; can be null | |
marketingContacts | integer | No | 0–100000000; can be null | |
marketingSendsPerMonth | integer | No | 0–1000000000; can be null | |
driveStorageGbPerUser | integer | No | 0–1000000; can be null | |
audience | "business" | "personal" | No | "business" | |
tagline | string | No | up to 200 characters; trimmed; can be null | |
priceMonthly | integer | No | 0–10000000; can be null | |
priceAnnual | integer | No | 0–100000000; can be null | |
priceTwoYear | integer | No | 0–200000000; can be null | |
offered | boolean | No | can be null | |
trialDays | integer | No | 0–90; can be null | |
apps | [] | No | up to 50 items; can be null | |
features | string[] | No | up to 50 items; each matches ^[a-z0-9-]{1,40}$; can be null | |
support | string | No | up to 120 characters; trimmed; can be null | |
highlights | string[] | No | up to 12 items; each 1–120 characters, trimmed; can be null | |
caityMessagesPerDay | integer | No | 0–100000; can be null | |
pipelineMinutes | integer | No | 0–10000000; can be null | |
pipelineOverage | boolean | No | can be null | |
pipelineConcurrency | integer | No | 0–500; can be null | |
pipelineMaxMinutes | integer | No | 1–2160; can be null | |
pipelineLogDays | integer | No | 1–400; can be null | |
pipelineArtifactDays | integer | No | 1–400; can be null | |
pipelineArtifactGb | integer | No | 0–100000; can be null | |
pipelineCacheGb | integer | No | 0–10000; can be null | |
keysMax | integer | No | 0–1000000; can be null | |
keyOpsPerMonth | integer | No | 0–1000000000; can be null | |
secretsMax | integer | No | 0–1000000; can be null | |
keyMonthlyPriceCents | integer | No | 0–1000000; can be null | |
keyOpsPer10kPriceCents | integer | No | 0–1000000; can be null |
Response 200
{
plan: {
features?: string[]
name: string
createdAt?: number
updatedAt?: number
planId: string
apps?: string[]
audience?: "personal" | "business"
tagline?: string
isDefault?: boolean
sortOrder?: number
priceMonthly?: number
priceAnnual?: number
priceTwoYear?: number
offered?: boolean
trialDays?: number
support?: string
highlights?: string[]
caityMessagesPerDay?: number
auditRetentionDays: number
mailSendPerDay?: number
mailMaxMessageMb?: number
mailMaxRecipients?: number
maxConnectors?: number
maxConnectorTools?: number
driveStorageGb?: number
driveStorageGbPerUser?: number
drivePersonalStorageGb?: number
vaultMaxItems?: number
crmMaxRecords?: number
marketingContacts?: number
marketingSendsPerMonth?: number
pipelineMinutes?: number
pipelineOverage?: boolean
pipelineConcurrency?: number
pipelineMaxMinutes?: number
pipelineLogDays?: number
pipelineArtifactDays?: number
pipelineArtifactGb?: number
pipelineCacheGb?: number
keysMax?: number
keyOpsPerMonth?: number
secretsMax?: number
keyMonthlyPriceCents?: number
keyOpsPer10kPriceCents?: number
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
GET /v1/platform/office-templates
Built-in templates of Documents, Sheets and Slides (Admin → Templates): every one, hidden ones included.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
templates: {
templateId: string
kind: "document" | "spreadsheet" | "presentation" | "video"
name: string
category?: string
sortOrder?: number
hidden?: boolean
}[]
}GET /v1/platform/video-presets
Video's export presets (Admin → Templates → Video presets): every one, hidden ones included.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
presets: {
id: string
label: string
description?: string
settings: {
container: "mp4" | "webm"
video: null | {
codec: "av1" | "avc" | "hevc" | "vp9"
width: number
height: number
frameRate: {
num: number
den: number
}
bitrate: number
bitrateMode: "variable" | "constant"
keyframeEvery: number
profile?: string
hardware: "no-preference" | "prefer-hardware" | "prefer-software"
}
audio: null | {
codec: "aac" | "opus"
sampleRate: 48000 | 44100
channels: 2 | 1
bitrate: number
}
timecodeOverlay?: boolean
loudness?: null | {
target: number
truePeak: number
}
}
sortOrder: number
hidden: boolean
builtIn: boolean
}[]
}PUT /v1/platform/video-presets/:presetId
Changes an export preset's label, description, settings, order or visibility, or adds one; the app sees it within a minute.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:presetId | Aircraft preset id (lowercase letters, digits, - and _); under video-presets, a video export preset's id (h264-1080p). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
label | string | No | 1–80 characters; trimmed |
description | string | No | up to 200 characters; trimmed; can be null |
sortOrder | integer | No | -1000–10000 |
hidden | boolean | No | |
settings | object | No | |
settings.container | "mp4" | "webm" | Yes | |
settings.video | object | Yes | can be null |
settings.video.codec | "avc" | "hevc" | "vp9" | "av1" | Yes | |
settings.video.width | integer | Yes | 0–16384 |
settings.video.height | integer | Yes | 0–16384 |
settings.video.frameRate | object | Yes | |
settings.video.frameRate.num | integer | Yes | 0–240000 |
settings.video.frameRate.den | integer | Yes | 1–1001 |
settings.video.bitrate | integer | Yes | 100000–500000000 |
settings.video.bitrateMode | "variable" | "constant" | Yes | |
settings.video.keyframeEvery | integer | Yes | ≥ 0 |
settings.video.profile | string | No | up to 40 characters |
settings.video.hardware | "no-preference" | "prefer-hardware" | "prefer-software" | Yes | |
settings.audio | object | Yes | can be null |
settings.audio.codec | "aac" | "opus" | Yes | |
settings.audio.sampleRate | 48000 | 44100 | Yes | |
settings.audio.channels | 1 | 2 | Yes | |
settings.audio.bitrate | integer | Yes | 16000–1000000 |
settings.timecodeOverlay | boolean | No | |
settings.loudness | object | No | can be null |
settings.loudness.target | number | Yes | -40–0 |
settings.loudness.truePeak | number | Yes | -10–0 |
Response 200
{
preset: {
id: string
label: string
description?: string
settings: {
container: "mp4" | "webm"
video: null | {
codec: "av1" | "avc" | "hevc" | "vp9"
width: number
height: number
frameRate: {
num: number
den: number
}
bitrate: number
bitrateMode: "variable" | "constant"
keyframeEvery: number
profile?: string
hardware: "no-preference" | "prefer-hardware" | "prefer-software"
}
audio: null | {
codec: "aac" | "opus"
sampleRate: 48000 | 44100
channels: 2 | 1
bitrate: number
}
timecodeOverlay?: boolean
loudness?: null | {
target: number
truePeak: number
}
}
sortOrder: number
hidden: boolean
builtIn: boolean
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
GET /v1/platform/office-fonts
The editors' fonts (Admin → Fonts): every family, hidden ones included, and whether the stage has the files.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
fonts: {
family: string
category: "serif" | "mono" | "display" | "sans"
sortOrder: number
hidden: boolean
standIn: boolean
}[]
hosted: boolean
}PATCH /v1/platform/office-fonts/:family
Recategorises, orders or hides a font; the menus see it within a minute.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:family | A font family's name as the catalogue lists it (Open Sans), URL-encoded. |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
category | "sans" | "serif" | "mono" | "display" | No | |
sortOrder | integer | No | -1000–10000 |
hidden | boolean | No |
Response 200
{
font: {
hidden: boolean
sortOrder: number
category: "serif" | "mono" | "display" | "sans"
family: string
standIn: boolean
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
PATCH /v1/platform/office-templates/:templateId
Renames, recategorises, orders or hides a built-in template; the galleries see it within a minute.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:templateId | A Marketing template's id (tpl_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | No | 1–80 characters; trimmed |
category | string | No | up to 40 characters; trimmed; can be null |
sortOrder | integer | No | -1000–1000 |
hidden | boolean | No |
Response 200
{
template: {
hidden: boolean
sortOrder: number
templateId: string
kind: "document" | "spreadsheet" | "presentation" | "video"
name: string
category?: string
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
GET /v1/platform/models
Chat models and the capabilities that shape their requests. Chat reads them within the cache TTL.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
models: {
region?: string
label: string
createdAt?: number
updatedAt?: number
api?: "anthropic" | "converse"
description?: string
enabled?: boolean
thinking?: "none" | "adaptive" | "budget"
isDefault?: boolean
sortOrder?: number
modelId: string
thinkingBudget?: number
effort?: "medium" | "low" | "high" | "xhigh" | "max"
maxTokens: number
eagerToolInput?: boolean
fallbackModelIds?: string[]
fallbackModelId?: string
compactAt?: number
titles?: boolean
crossRegion?: boolean
accessFallback?: boolean
}[]
}PUT /v1/platform/models/:modelId
Creates or replaces a chat model in the catalog: label, whether it's enabled or the default, thinking and effort settings, output tokens and fallback model. Making a model the default clears the flag on the others.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:modelId | Bedrock base model id, e.g. anthropic.claude-opus-5-5. |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
label | string | Yes | 1–100 characters; trimmed | |
description | string | No | "" | up to 120 characters; trimmed |
enabled | boolean | No | true | |
isDefault | boolean | No | false | |
sortOrder | integer | No | 0 | |
thinking | "adaptive" | "budget" | "none" | No | "adaptive" | |
thinkingBudget | integer | No | 1024–128000; can be null | |
effort | "low" | "medium" | "high" | "xhigh" | "max" | No | can be null | |
maxTokens | integer | Yes | 1024–128000 | |
eagerToolInput | boolean | No | true | |
fallbackModelIds | string[] | No | up to 8 items; each matches ^[a-z0-9][a-z0-9.:-]{2,127}$ | |
fallbackModelId | string | No | matches ^[a-z0-9][a-z0-9.:-]{2,127}$; can be null | |
compactAt | integer | No | 50000–1000000; can be null | |
titles | boolean | No | ||
api | "anthropic" | "converse" | No | ||
region | string | No | matches ^[a-z]{2}(-[a-z]+)+-\d$; trimmed; can be null | |
crossRegion | boolean | No | ||
accessFallback | boolean | No |
Also checked: Budget thinking needs a thinking budget below the max tokens The default model must be enabled
Response 200
{
model: {
region?: string
label: string
createdAt?: number
updatedAt?: number
api?: "anthropic" | "converse"
description?: string
enabled?: boolean
thinking?: "none" | "adaptive" | "budget"
isDefault?: boolean
sortOrder?: number
modelId: string
thinkingBudget?: number
effort?: "medium" | "low" | "high" | "xhigh" | "max"
maxTokens: number
eagerToolInput?: boolean
fallbackModelIds?: string[]
fallbackModelId?: string
compactAt?: number
titles?: boolean
crossRegion?: boolean
accessFallback?: boolean
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
400 | A model can't fall back to itself |
400 | A fallback is listed twice |
400 | Unknown fallback model |
400 | Set another model as the default first |
GET /v1/platform/flight-coverage
Areas the Maps flight poller always covers (centre, radius, label, enabled), besides watches and areas people are viewing.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
areas: {
label: string
createdAt?: number
updatedAt?: number
lat: number
lon: number
enabled?: boolean
sortOrder?: number
areaId: string
radiusNm: number
}[]
}PUT /v1/platform/flight-coverage/:areaId
Creates or replaces a standing coverage area. The poller picks it up within a minute; radius is capped at the ADS-B source's maximum.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:areaId | Standing coverage area id. |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
label | string | Yes | 1–64 characters; trimmed | |
lat | number | Yes | -90–90 | |
lon | number | Yes | -180–180 | |
radiusNm | number | Yes | 5–250 | |
enabled | boolean | No | true | |
sortOrder | integer | No | 0 |
Response 200
{
area: {
label: string
createdAt?: number
updatedAt?: number
lat: number
lon: number
enabled?: boolean
sortOrder?: number
areaId: string
radiusNm: number
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
GET /v1/platform/flight-sources
Maps: the ADS-B sources the flight poller uses, in order (seeded adsb.lol, adsb.fi, ADS-B Exchange as a capped last resort). The poller reads them within the cache TTL.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
sources: {
createdAt?: number
updatedAt?: number
sourceId: string
enabled?: boolean
sortOrder?: number
use?: "all" | "last_resort"
dailyRequests?: number
monthlyRequests?: number
}[]
known: {
sourceId: "adsblol" | "adsbfi" | "airplaneslive" | "opensky" | "adsbexchange"
label: string
terms: string
maxRadiusNm: number
rate: {
burst: number
perMinute: number
}
}[]
}PUT /v1/platform/flight-sources/:sourceId
Changes an ADS-B source: its place in the order, whether it's used, and its daily and monthly request budgets.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:sourceId | ADS-B source id (adsblol, adsbfi, adsbexchange, …). |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
enabled | boolean | No | true | |
sortOrder | integer | No | 0 | |
use | "all" | "last_resort" | No | "all" | |
dailyRequests | integer | No | 0–100000 | |
monthlyRequests | integer | No | 0–10000000 |
Response 200
{
source: {
createdAt?: number
updatedAt?: number
sourceId: string
enabled?: boolean
sortOrder?: number
use?: "all" | "last_resort"
dailyRequests?: number
monthlyRequests?: number
}
}Errors
| Status | Message |
|---|---|
400 | Unknown source; one of … |
GET /v1/platform/incident-sources
Maps: the emergency feeds the incidents poller reads, in order (rows: the seed's until one is saved), the parsers it knows (formats) and how each feed did last (status, from the live picture). The poller reads them within the cache TTL.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
sources: {
url: string
format: string
label: string
attribution: string
state: string
sourceId: string
enabled: boolean
sortOrder: number
pollSeconds: number
licence: string
licenceUrl?: string
homepage?: string
dedupe: boolean
}[]
saved: boolean
formats: {
id: string
label: string
}[]
status: {
sourceId: string
label: string
state: "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA" | "AU"
attribution: string
licence: string
licenceUrl?: string
homepage?: string
count: number
ok: boolean
checkedAt?: number
changedAt?: number
error?: string
}[]
updatedAt: null | number
}PUT /v1/platform/incident-sources/:sourceId
Adds or changes an emergency feed: label, parser (format), https URL, state, order, on or off, poll interval (30–3,600 s), credit line, licence and the agency's page. The first save also writes the seed's other feeds so they stay in use.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:sourceId | ADS-B source id (adsblol, adsbfi, adsbexchange, …). |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
label | string | Yes | 1–80 characters; trimmed | |
format | string | Yes | up to 40 characters | |
url | string | Yes | up to 1,000 characters; URL | |
state | "ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA" | "AU" | Yes | ||
enabled | boolean | No | true | |
sortOrder | integer | No | 0 | |
pollSeconds | integer | No | 60 | 30–3600 |
attribution | string | Yes | 1–300 characters; trimmed | |
licence | string | Yes | 1–120 characters; trimmed | |
licenceUrl | string | No | up to 500 characters; URL | |
homepage | string | No | up to 500 characters; URL | |
dedupe | boolean | No | true |
Response 200
{
source: {
url: string
format: string
label: string
createdAt?: number
updatedAt?: number
attribution: string
state: string
sourceId: string
enabled?: boolean
sortOrder?: number
pollSeconds?: number
licence: string
licenceUrl?: string
homepage?: string
dedupe?: boolean
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
400 | Unknown format; one of … |
400 | Feeds are read over https. |
DELETE /v1/platform/incident-sources/:sourceId
Removes an emergency feed. Its incidents leave the map at the next poll without alerting anyone.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:sourceId | ADS-B source id (adsblol, adsbfi, adsbexchange, …). |
Response 200
{
ok: true
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
GET /v1/platform/limits
Maps: aircraft presets watches target ({ kind: "preset", value: <presetId> }), e.g. police and emergency callsigns. Targets are hex, registration, callsign or type, * for prefixes. The poller reads them within the cache TTL.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
limits: {
value: null | number
updatedAt: null | number
id: string
group: string
label: string
description?: string
default: number
min: number
max: number
step?: number
}[]
}PUT /v1/platform/limits/:limitId
Sets a limit (null: back to its default). Readers see it within a minute.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:limitId | A limit's id, e.g. mirage.discord.historyMax. |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
value | number | Yes | can be null |
Response 200
{
limit: {
value: null | number
updatedAt: number
id: string
group: string
label: string
description?: string
default: number
min: number
max: number
step?: number
}
}Errors
| Status | Message |
|---|---|
400 | …: between … and ……. |
404 | Unknown limit |
GET /v1/platform/marketing/sms-prices
What a Marketing text part costs per destination (currency, prices of prefix, country, perPart): the prices in force, the built-in ones, and whether any are stored. Platform staff (platform:admin).
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
prices: {
currency: string
prices: {
prefix: string
country: string
perPart: number
}[]
}
defaults: {
currency: string
prices: {
prefix: string
country: string
perPart: number
}[]
}
stored: boolean
updatedAt: null | number
updatedBy: null | string
}PUT /v1/platform/marketing/sms-prices
Replaces the text prices (prices; null puts the built-in ones back). Journeys' text steps show costs from them within a minute. Audited. Platform staff.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
prices | object | Yes | can be null |
prices.currency | string | Yes | matches ^[A-Z]{3}$ |
prices.prices | object[] | Yes | up to 300 items |
prices.prices[].prefix | string | Yes | matches ^\+[1-9]\d{0,5}$ |
prices.prices[].country | string | Yes | 1–60 characters; trimmed |
prices.prices[].perPart | number | Yes | 0–5 |
Response 200
{
prices: {
currency: string
prices: {
prefix: string
country: string
perPart: number
}[]
}
defaults: {
currency: string
prices: {
prefix: string
country: string
perPart: number
}[]
}
stored: boolean
updatedAt: null | number
updatedBy: null | string
}GET /v1/platform/aircraft-presets
Aircraft presets flight watches can target ({ kind: "preset", value: <presetId> }): label, targets ({ kind: hex | registration | callsign | type, value }, * for prefixes), enabled, sortOrder.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
presets: {
targets: unknown
label: string
createdAt?: number
updatedAt?: number
enabled?: boolean
sortOrder?: number
presetId: string
}[]
}PUT /v1/platform/aircraft-presets/:presetId
Creates or replaces an aircraft preset (label, targets, enabled, sortOrder). Watches that use it pick it up within a minute.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:presetId | Aircraft preset id (lowercase letters, digits, - and _); under video-presets, a video export preset's id (h264-1080p). |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
label | string | Yes | 1–64 characters; trimmed | |
targets | object[] | Yes | 1–200 items | |
targets[].kind | "hex" | "registration" | "callsign" | "type" | Yes | ||
targets[].value | string | Yes | 1–16 characters; matches ^[A-Za-z0-9~*-]+$; trimmed | |
enabled | boolean | No | true | |
sortOrder | integer | No | 0 |
Response 200
{
preset: {
targets: {
kind: "type" | "hex" | "callsign" | "registration"
value: string
}[]
label: string
createdAt?: number
updatedAt?: number
enabled?: boolean
sortOrder?: number
presetId: string
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
DELETE /v1/platform/aircraft-presets/:presetId
Removes an aircraft preset; watches that named it stop matching through it.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:presetId | Aircraft preset id (lowercase letters, digits, - and _); under video-presets, a video export preset's id (h264-1080p). |
Response 200
{
ok: true
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
404 | Preset not found |
GET /v1/platform/mail-category-rules
Rules that sort received mail into inbox categories, in order: match (domain, local, subject or header), value, category (primary, transactions, updates, promotions), enabled, sortOrder.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Response 200
{
rules: {
value: string
createdAt?: number
updatedAt?: number
match: "header" | "domain" | "local" | "subject"
ruleId: string
category: "primary" | "transactions" | "updates" | "promotions"
enabled?: boolean
sortOrder?: number
}[]
}POST /v1/platform/mail-category-rules/test
Classifies a pasted message with the saved rules, exactly as delivery would for a sender the mailbox doesn't know: pasted headers (or a whole message), or From and Subject.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
headers | string | No | up to 64,000 characters |
from | string | No | up to 320 characters |
subject | string | No | up to 998 characters |
Also checked: Paste headers, or give a From address or a subject.
Response 200
{
result: {
category: "primary" | "transactions" | "updates" | "promotions"
reason: string
from: string
subject: string
domain: string
local: string
bulk: boolean
rule?: {
ruleId: string
match: "header" | "domain" | "local" | "subject"
value: string
category: "primary" | "transactions" | "updates" | "promotions"
enabled: boolean
sortOrder: number
}
}
}PUT /v1/platform/mail-category-rules/:ruleId
Creates or replaces a mail category rule. Mail received from then on (within a minute) follows it; mail already received keeps its category.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:ruleId | Rule id: a storage lifecycle rule (as returned by the settings or lifecycle routes), an issue automation rule (rul_…) or a Mirage AutoMod rule (mar_…). |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
match | "domain" | "local" | "subject" | "header" | Yes | ||
value | string | Yes | 1–120 characters; trimmed; lowercased | |
category | "primary" | "transactions" | "updates" | "promotions" | Yes | ||
enabled | boolean | No | true | |
sortOrder | integer | No | 0 |
Response 200
{
rule: {
value: string
createdAt?: number
updatedAt?: number
match: "header" | "domain" | "local" | "subject"
ruleId: string
category: "primary" | "transactions" | "updates" | "promotions"
enabled?: boolean
sortOrder?: number
}
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
DELETE /v1/platform/mail-category-rules/:ruleId
Removes a mail category rule.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:ruleId | Rule id: a storage lifecycle rule (as returned by the settings or lifecycle routes), an issue automation rule (rul_…) or a Mirage AutoMod rule (mar_…). |
Response 200
{
ok: true
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
404 | Rule not found |
DELETE /v1/platform/flight-coverage/:areaId
Removes a standing coverage area.
Auth: user access token or platform agent key · Scope: platform:admin in the platform organization
| Path parameter | Description |
|---|---|
:areaId | Standing coverage area id. |
Response 200
{
ok: true
}Errors
| Status | Message |
|---|---|
400 | Invalid id: lowercase letters, digits and hyphens. |
404 | Coverage area not found |