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 parameterTypeRequiredDefaultNotes
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 parameterTypeRequiredDefaultNotes
daysintegerNo71–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 parameterTypeRequiredNotes
monthstringNomatches ^\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

FieldTypeRequiredNotes
version1Yes
commitstringNomatches ^[0-9a-f]{7,64}$
branchstringNo1–255 characters
runUrlstringNoup to 2,000 characters; URL
source"ci" | "local"Yes
startedAtintegerYes> 0
finishedAtintegerYes> 0
packagesobject[]Yes1–300 items
packages[].namestringYes1–214 characters
packages[].dirstringYesup to 200 characters; matches ^[A-Za-z0-9][A-Za-z0-9._-]*(\/[A-Za-z0-9._-]+)*$
packages[].status"passed" | "failed" | "error"Yes
packages[].testsobjectYes
packages[].tests.totalintegerYes0–100000000
packages[].tests.passedintegerYes0–100000000
packages[].tests.failedintegerYes0–100000000
packages[].tests.skippedintegerYes0–100000000
packages[].tests.todointegerYes0–100000000
packages[].tests.cancelledintegerYes0–100000000
packages[].durationMsintegerYes0–100000000
packages[].linesobjectYes
packages[].lines.totalintegerYes0–100000000
packages[].lines.coveredintegerYes0–100000000
packages[].branchesobjectYes
packages[].branches.totalintegerYes0–100000000
packages[].branches.coveredintegerYes0–100000000
packages[].functionsobjectYes
packages[].functions.totalintegerYes0–100000000
packages[].functions.coveredintegerYes0–100000000
packages[].filesobjectYes
packages[].files.loadedintegerYes0–100000000
packages[].files.sourceintegerYes0–100000000
packages[].leastCoveredobject[]Yesup to 25 items
packages[].leastCovered[].pathstringYes1–500 characters
packages[].leastCovered[].linesobjectYes
packages[].leastCovered[].lines.totalintegerYes0–100000000
packages[].leastCovered[].lines.coveredintegerYes0–100000000
packages[].leastCovered[].branchesobjectYes
packages[].leastCovered[].branches.totalintegerYes0–100000000
packages[].leastCovered[].branches.coveredintegerYes0–100000000
packages[].leastCovered[].functionsobjectYes
packages[].leastCovered[].functions.totalintegerYes0–100000000
packages[].leastCovered[].functions.coveredintegerYes0–100000000
packages[].untestedstring[]Yesup to 50 items; each 1–500 characters
packages[].errorstringNoup to 2,000 characters
packages[].cachedbooleanNo
totalsany JSONNo

Response 201

{
  runId: string
}

Errors

StatusMessage
503The 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

StatusMessage
403Missing 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 parameterTypeRequiredNotes
runstringNoup 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

StatusMessage
404No 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 parameterDescription
:runIdCoverage run id (cov_…).
Query parameterTypeRequiredNotes
dirany JSONYes

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

StatusMessage
404No 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 parameterDescription
:userIdA 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

StatusMessage
400outcome is 16_plus or under_16.
403Age 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 parameterDescription
:postIdA Feed post id (pst_…).
Query parameterTypeRequiredNotes
reasonstringNo

Response 204 with no body.

Errors

StatusMessage
403Posts 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

StatusMessage
403Age 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

StatusMessage
403Age 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 parameterTypeRequiredDefaultNotes
emailstringNo""

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

StatusMessage
403Age checks are changed by people.
404Nobody 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 parameterDescription
:userIdA 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

StatusMessage
403Age 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 parameterDescription
:userIdA 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

StatusMessage
403Age 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 parameterDescription
:metricIdA 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 parameterDescription
:metricIdA Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them.

Request body

FieldTypeRequiredNotes
enabledbooleanYes

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 parameterDescription
:effectIdA 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 parameterDescription
:effectIdA profile effect's id from the catalogue (starlight, northern-lights): 2–40 lowercase letters, digits or hyphens.

Request body

FieldTypeRequiredNotes
enabledbooleanYes

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

StatusMessage
400Send 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 parameterDescription
:whoA 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 parameterDescription
:whoA person's user id (usr_…) or an email address, as listed in the request's participants.

Request body

FieldTypeRequiredNotes
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 parameterDescription
:whoA 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 parameterDescription
:whoA person's user id (usr_…) or an email address, as listed in the request's participants.

Request body

FieldTypeRequiredNotes
planIdstringYesmatches ^[a-z0-9][a-z0-9-]{1,62}$
untilintegerNo> 0; can be null
notestringNoup 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 parameterDescription
:whoA 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 parameterTypeRequiredNotes
statusstringNo
beforestringNo

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 parameterDescription
:reportIdA 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

StatusMessage
403Reports 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

StatusMessage
503No 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 parameterDescription
:labelHosted runner type label, e.g. linux-arm64.

Request body

FieldTypeRequiredDefaultNotes
descriptionstringNo""up to 200 characters; trimmed
project"lambda" | "container"Yes
environmentType"ARM_LAMBDA_CONTAINER" | "LINUX_LAMBDA_CONTAINER" | "ARM_CONTAINER" | "LINUX_CONTAINER"Yes
computeTypestringYesmatches ^BUILD_(LAMBDA_\d+GB|GENERAL1_[A-Z0-9]+)$
imagestringYes3–300 characters; trimmed
multipliernumberNo10–100
maxMinutesintegerYes1–2160
dockerbooleanNofalse
orgsstring[]No[]up to 100 items; each matches ^org_[0-9a-z]+$
isDefaultbooleanNofalse
enabledbooleanNotrue
sortOrderintegerNo0

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

StatusMessage
400Labels are lower case letters, numbers, '.' and '-' (not hosted, self-hosted or platform-deploy)
400Lambda compute: a Lambda environment type, at most 15 minutes, no Docker
400Container runners use ARM_CONTAINER or LINUX_CONTAINER
503No 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 parameterDescription
:labelHosted runner type label, e.g. linux-arm64.

Response 204 with no body.

Errors

StatusMessage
503No 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

StatusMessage
503No 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

FieldTypeRequiredNotes
maxMatrixJobsintegerYes1–1000
maxJobsintegerYes1–1000
maxLogMbintegerYes1–1024
maxArtifactMbintegerYes1–5120
maxCacheMbintegerYes1–5120
maxWorkflowKbintegerYes16–4096
scheduleMinMinutesintegerYes1–1440
approvalDaysintegerYes1–90
queueHoursintegerYes1–168
heartbeatMinutesintegerYes1–60
defaultArtifactDaysintegerYes1–400
cacheUnusedDaysintegerYes1–90
secretKbintegerYes1–64
maxSecretsintegerYes1–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

StatusMessage
503No 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

StatusMessage
400Unknown filter.
403Feedback 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 parameterDescription
:reportIdA 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

StatusMessage
403Feedback 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 parameterDescription
:reportIdA 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

StatusMessage
403Feedback 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 parameterDescription
:reportIdA 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

StatusMessage
403Feedback 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 parameterDescription
:reportIdA 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

StatusMessage
400Choose a space.
403Feedback is reviewed by people.
403Missing 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 parameterDescription
:reportIdA report's id: a Mirage report's (mrp_…) or a feedback report's (fbk_…).

Response 200

{
  reportId: string
}

Errors

StatusMessage
403Feedback 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

FieldTypeRequiredDefaultNotes
namestringYes1–64 characters; trimmed
slugstringYesmatches ^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){1,38}$; trimmed
ownerEmailstringYesup to 254 characters; email address
planIdstringNomatches ^[a-z0-9][a-z0-9-]{1,62}$; can be null
subsidisedbooleanNofalse

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

StatusMessage
400Unknown plan
403Onboarding 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 parameterDescription
:orgIdOrganization id (org_…).

Request body

FieldTypeRequiredNotes
planIdstringNomatches ^[a-z0-9][a-z0-9-]{1,62}$; can be null
subsidisedbooleanNo

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

StatusMessage
400Unknown plan
404Organization 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 parameterTypeRequiredDefaultNotes
cursorstringNoup to 4,096 characters
actionstringNomatches ^[a-z_]{1,32}(\.[a-z_]{1,32})?$
actorstringNo1–512 characters
targetstringNomatches ^[a-z_]{1,32}:[^\s]{1,512}$
limitintegerNo501–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

StatusMessage
400Invalid cursor
403Missing 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 parameterDescription
:clientIdSign-in client id (cli_…).

Response 204 with no body.

Errors

StatusMessage
404Client 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 parameterDescription
:regionIdAWS region id, e.g. us-west-1.

Request body

FieldTypeRequiredDefaultNotes
namestringYes1–100 characters
status"active" | "disabled"No"active"
unavailablestring[]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

StatusMessage
400Invalid 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 parameterDescription
:locationIdLocation id: a platform location, or under Maps an org's alert location (mlc_…).

Request body

FieldTypeRequiredDefaultNotes
labelstringYes1–100 characters
regionsstring[]Yes1–10 items; each matches ^[a-z0-9][a-z0-9-]{1,62}$
status"active" | "preview" | "disabled"No"active"
isDefaultbooleanNofalse
sortOrderintegerNo0

Response 200

{
  location: {
    label: string
    status?: "active" | "disabled" | "preview"
    createdAt?: number
    updatedAt?: number
    locationId: string
    isDefault?: boolean
    regions: string[]
    sortOrder?: number
  }
}

Errors

StatusMessage
400Invalid 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

FieldTypeRequiredNotes
codestringYes3–40 characters; trimmed
namestringNoup to 80 characters; trimmed
couponIdstringNomatches ^[A-Za-z0-9_-]{1,100}$
percentOffintegerNo1–100
amountOffintegerNo≥ 1
duration"once" | "repeating" | "forever"No
durationInMonthsintegerNo1–36
maxRedemptionsintegerNo1–1000000
expiresAtintegerNo> 0
planIdsstring[]Noup to 20 items; each matches ^[a-z0-9][a-z0-9-]{1,62}$
intervals("two-year" | "year" | "month")[]Noup to 3 items
emailstringNoup to 254 characters; email address
ownerIdstringNomatches ^(org|usr)_[A-Za-z0-9]{6,40}$
firstTimeOnlybooleanNo
minimumAmountintegerNo≥ 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

StatusMessage
403You 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 parameterDescription
:promotionCodeIdA promotion code's id (the payment provider's, promo_…).

Request body

FieldTypeRequiredNotes
activebooleanYes

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

StatusMessage
400Invalid id: lowercase letters, digits and hyphens.
403You 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 parameterDescription
:couponIdA coupon's id (the payment provider's).

Response 200

{
  ok: true
}

Errors

StatusMessage
400Invalid id: lowercase letters, digits and hyphens.
403You 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 parameterTypeRequiredNotes
promotionCodeIdstringNo

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

StatusMessage
400Invalid 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

FieldTypeRequiredNotes
roles("owner" | "admin" | "developer" | "viewer")[]Yesup to 4 items
emailsstring[]Yesup to 100 items; each up to 254 characters, email address

Response 200

{
  creators: {
    roles: ("owner" | "admin" | "developer" | "viewer")[]
    emails: string[]
  }
}

Errors

StatusMessage
403Only 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 parameterDescription
:planIdPlan id.

Request body

FieldTypeRequiredDefaultNotes
namestringYes1–100 characters; trimmed
auditRetentionDaysintegerYes1–3650
isDefaultbooleanNofalse
sortOrderintegerNo0
mailSendPerDayintegerNo0–1000000; can be null
mailMaxMessageMbintegerNo1–40; can be null
mailMaxRecipientsintegerNo1–1000; can be null
maxConnectorsintegerNo0–1000; can be null
maxConnectorToolsintegerNo0–1000; can be null
driveStorageGbintegerNo0–1000000; can be null
drivePersonalStorageGbintegerNo0–1000000; can be null
vaultMaxItemsintegerNo0–1000000; can be null
crmMaxRecordsintegerNo0–1000000000; can be null
marketingContactsintegerNo0–100000000; can be null
marketingSendsPerMonthintegerNo0–1000000000; can be null
driveStorageGbPerUserintegerNo0–1000000; can be null
audience"business" | "personal"No"business"
taglinestringNoup to 200 characters; trimmed; can be null
priceMonthlyintegerNo0–10000000; can be null
priceAnnualintegerNo0–100000000; can be null
priceTwoYearintegerNo0–200000000; can be null
offeredbooleanNocan be null
trialDaysintegerNo0–90; can be null
apps[]Noup to 50 items; can be null
featuresstring[]Noup to 50 items; each matches ^[a-z0-9-]{1,40}$; can be null
supportstringNoup to 120 characters; trimmed; can be null
highlightsstring[]Noup to 12 items; each 1–120 characters, trimmed; can be null
caityMessagesPerDayintegerNo0–100000; can be null
pipelineMinutesintegerNo0–10000000; can be null
pipelineOveragebooleanNocan be null
pipelineConcurrencyintegerNo0–500; can be null
pipelineMaxMinutesintegerNo1–2160; can be null
pipelineLogDaysintegerNo1–400; can be null
pipelineArtifactDaysintegerNo1–400; can be null
pipelineArtifactGbintegerNo0–100000; can be null
pipelineCacheGbintegerNo0–10000; can be null
keysMaxintegerNo0–1000000; can be null
keyOpsPerMonthintegerNo0–1000000000; can be null
secretsMaxintegerNo0–1000000; can be null
keyMonthlyPriceCentsintegerNo0–1000000; can be null
keyOpsPer10kPriceCentsintegerNo0–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

StatusMessage
400Invalid 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 parameterDescription
:presetIdAircraft preset id (lowercase letters, digits, - and _); under video-presets, a video export preset's id (h264-1080p).

Request body

FieldTypeRequiredNotes
labelstringNo1–80 characters; trimmed
descriptionstringNoup to 200 characters; trimmed; can be null
sortOrderintegerNo-1000–10000
hiddenbooleanNo
settingsobjectNo
settings.container"mp4" | "webm"Yes
settings.videoobjectYescan be null
settings.video.codec"avc" | "hevc" | "vp9" | "av1"Yes
settings.video.widthintegerYes0–16384
settings.video.heightintegerYes0–16384
settings.video.frameRateobjectYes
settings.video.frameRate.numintegerYes0–240000
settings.video.frameRate.denintegerYes1–1001
settings.video.bitrateintegerYes100000–500000000
settings.video.bitrateMode"variable" | "constant"Yes
settings.video.keyframeEveryintegerYes≥ 0
settings.video.profilestringNoup to 40 characters
settings.video.hardware"no-preference" | "prefer-hardware" | "prefer-software"Yes
settings.audioobjectYescan be null
settings.audio.codec"aac" | "opus"Yes
settings.audio.sampleRate48000 | 44100Yes
settings.audio.channels1 | 2Yes
settings.audio.bitrateintegerYes16000–1000000
settings.timecodeOverlaybooleanNo
settings.loudnessobjectNocan be null
settings.loudness.targetnumberYes-40–0
settings.loudness.truePeaknumberYes-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

StatusMessage
400Invalid 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 parameterDescription
:familyA font family's name as the catalogue lists it (Open Sans), URL-encoded.

Request body

FieldTypeRequiredNotes
category"sans" | "serif" | "mono" | "display"No
sortOrderintegerNo-1000–10000
hiddenbooleanNo

Response 200

{
  font: {
    hidden: boolean
    sortOrder: number
    category: "serif" | "mono" | "display" | "sans"
    family: string
    standIn: boolean
  }
}

Errors

StatusMessage
400Invalid 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 parameterDescription
:templateIdA Marketing template's id (tpl_…).

Request body

FieldTypeRequiredNotes
namestringNo1–80 characters; trimmed
categorystringNoup to 40 characters; trimmed; can be null
sortOrderintegerNo-1000–1000
hiddenbooleanNo

Response 200

{
  template: {
    hidden: boolean
    sortOrder: number
    templateId: string
    kind: "document" | "spreadsheet" | "presentation" | "video"
    name: string
    category?: string
  }
}

Errors

StatusMessage
400Invalid 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 parameterDescription
:modelIdBedrock base model id, e.g. anthropic.claude-opus-5-5.

Request body

FieldTypeRequiredDefaultNotes
labelstringYes1–100 characters; trimmed
descriptionstringNo""up to 120 characters; trimmed
enabledbooleanNotrue
isDefaultbooleanNofalse
sortOrderintegerNo0
thinking"adaptive" | "budget" | "none"No"adaptive"
thinkingBudgetintegerNo1024–128000; can be null
effort"low" | "medium" | "high" | "xhigh" | "max"Nocan be null
maxTokensintegerYes1024–128000
eagerToolInputbooleanNotrue
fallbackModelIdsstring[]Noup to 8 items; each matches ^[a-z0-9][a-z0-9.:-]{2,127}$
fallbackModelIdstringNomatches ^[a-z0-9][a-z0-9.:-]{2,127}$; can be null
compactAtintegerNo50000–1000000; can be null
titlesbooleanNo
api"anthropic" | "converse"No
regionstringNomatches ^[a-z]{2}(-[a-z]+)+-\d$; trimmed; can be null
crossRegionbooleanNo
accessFallbackbooleanNo

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

StatusMessage
400Invalid id: lowercase letters, digits and hyphens.
400A model can't fall back to itself
400A fallback is listed twice
400Unknown fallback model
400Set 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 parameterDescription
:areaIdStanding coverage area id.

Request body

FieldTypeRequiredDefaultNotes
labelstringYes1–64 characters; trimmed
latnumberYes-90–90
lonnumberYes-180–180
radiusNmnumberYes5–250
enabledbooleanNotrue
sortOrderintegerNo0

Response 200

{
  area: {
    label: string
    createdAt?: number
    updatedAt?: number
    lat: number
    lon: number
    enabled?: boolean
    sortOrder?: number
    areaId: string
    radiusNm: number
  }
}

Errors

StatusMessage
400Invalid 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 parameterDescription
:sourceIdADS-B source id (adsblol, adsbfi, adsbexchange, …).

Request body

FieldTypeRequiredDefaultNotes
enabledbooleanNotrue
sortOrderintegerNo0
use"all" | "last_resort"No"all"
dailyRequestsintegerNo0–100000
monthlyRequestsintegerNo0–10000000

Response 200

{
  source: {
    createdAt?: number
    updatedAt?: number
    sourceId: string
    enabled?: boolean
    sortOrder?: number
    use?: "all" | "last_resort"
    dailyRequests?: number
    monthlyRequests?: number
  }
}

Errors

StatusMessage
400Unknown 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 parameterDescription
:sourceIdADS-B source id (adsblol, adsbfi, adsbexchange, …).

Request body

FieldTypeRequiredDefaultNotes
labelstringYes1–80 characters; trimmed
formatstringYesup to 40 characters
urlstringYesup to 1,000 characters; URL
state"ACT" | "NSW" | "NT" | "QLD" | "SA" | "TAS" | "VIC" | "WA" | "AU"Yes
enabledbooleanNotrue
sortOrderintegerNo0
pollSecondsintegerNo6030–3600
attributionstringYes1–300 characters; trimmed
licencestringYes1–120 characters; trimmed
licenceUrlstringNoup to 500 characters; URL
homepagestringNoup to 500 characters; URL
dedupebooleanNotrue

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

StatusMessage
400Invalid id: lowercase letters, digits and hyphens.
400Unknown format; one of …
400Feeds 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 parameterDescription
:sourceIdADS-B source id (adsblol, adsbfi, adsbexchange, …).

Response 200

{
  ok: true
}

Errors

StatusMessage
400Invalid 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 parameterDescription
:limitIdA limit's id, e.g. mirage.discord.historyMax.

Request body

FieldTypeRequiredNotes
valuenumberYescan 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

StatusMessage
400…: between … and …….
404Unknown 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

FieldTypeRequiredNotes
pricesobjectYescan be null
prices.currencystringYesmatches ^[A-Z]{3}$
prices.pricesobject[]Yesup to 300 items
prices.prices[].prefixstringYesmatches ^\+[1-9]\d{0,5}$
prices.prices[].countrystringYes1–60 characters; trimmed
prices.prices[].perPartnumberYes0–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 parameterDescription
:presetIdAircraft preset id (lowercase letters, digits, - and _); under video-presets, a video export preset's id (h264-1080p).

Request body

FieldTypeRequiredDefaultNotes
labelstringYes1–64 characters; trimmed
targetsobject[]Yes1–200 items
targets[].kind"hex" | "registration" | "callsign" | "type"Yes
targets[].valuestringYes1–16 characters; matches ^[A-Za-z0-9~*-]+$; trimmed
enabledbooleanNotrue
sortOrderintegerNo0

Response 200

{
  preset: {
    targets: {
      kind: "type" | "hex" | "callsign" | "registration"
      value: string
    }[]
    label: string
    createdAt?: number
    updatedAt?: number
    enabled?: boolean
    sortOrder?: number
    presetId: string
  }
}

Errors

StatusMessage
400Invalid 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 parameterDescription
:presetIdAircraft preset id (lowercase letters, digits, - and _); under video-presets, a video export preset's id (h264-1080p).

Response 200

{
  ok: true
}

Errors

StatusMessage
400Invalid id: lowercase letters, digits and hyphens.
404Preset 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

FieldTypeRequiredNotes
headersstringNoup to 64,000 characters
fromstringNoup to 320 characters
subjectstringNoup 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 parameterDescription
:ruleIdRule 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

FieldTypeRequiredDefaultNotes
match"domain" | "local" | "subject" | "header"Yes
valuestringYes1–120 characters; trimmed; lowercased
category"primary" | "transactions" | "updates" | "promotions"Yes
enabledbooleanNotrue
sortOrderintegerNo0

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

StatusMessage
400Invalid 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 parameterDescription
:ruleIdRule 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

StatusMessage
400Invalid id: lowercase letters, digits and hyphens.
404Rule 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 parameterDescription
:areaIdStanding coverage area id.

Response 200

{
  ok: true
}

Errors

StatusMessage
400Invalid id: lowercase letters, digits and hyphens.
404Coverage area not found