0d236f57fb
Provide App Attest-bound, one-time onboarding AI access without creating accounts, with durable replay protection and production deployment safeguards.
2786 lines
105 KiB
YAML
2786 lines
105 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: OSG Account Server API
|
|
version: 0.1.0
|
|
description: |
|
|
Account, credit, referral, integrity, and managed AI APIs for OSGKeyboard.
|
|
Local features and user-owned API keys remain independent of this service.
|
|
servers:
|
|
- url: https://account.osglab.com
|
|
security:
|
|
- bearerAuth: []
|
|
paths:
|
|
/health:
|
|
get:
|
|
security: []
|
|
summary: Compatibility health check
|
|
responses:
|
|
"200": { $ref: "#/components/responses/HealthUp" }
|
|
/health/live:
|
|
get:
|
|
security: []
|
|
summary: Process liveness
|
|
responses:
|
|
"200": { $ref: "#/components/responses/HealthUp" }
|
|
/health/ready:
|
|
get:
|
|
security: []
|
|
summary: Database and migration readiness
|
|
responses:
|
|
"200": { $ref: "#/components/responses/HealthUp" }
|
|
"503":
|
|
description: Database is unavailable or migrations failed.
|
|
/v1/auth/apple:
|
|
post:
|
|
security: []
|
|
summary: Exchange Sign in with Apple credentials for an OSG session
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AppleSignInRequest" }
|
|
responses:
|
|
"200":
|
|
description: Authenticated session
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/SessionTokenEnvelope" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/auth/refresh:
|
|
post:
|
|
security: []
|
|
summary: Rotate a refresh token
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [refreshToken]
|
|
properties:
|
|
refreshToken: { type: string, minLength: 32 }
|
|
responses:
|
|
"200":
|
|
description: Rotated session
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/SessionTokenEnvelope" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/auth/logout:
|
|
post:
|
|
summary: Revoke the current session family
|
|
responses:
|
|
"204": { description: Session revoked }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/analytics/events:
|
|
post:
|
|
security:
|
|
- {}
|
|
- bearerAuth: []
|
|
summary: Idempotently accept privacy-minimized product events
|
|
description: |
|
|
Accepts pre-login or authenticated client events. The random
|
|
installation UUID is stored only as a digest. When a valid bearer
|
|
session is supplied, the installation is linked to the account and is
|
|
deleted with that account. Audio, user text, prompts, transcripts,
|
|
model output, credentials and arbitrary properties are never accepted.
|
|
New clients must send installationId once at the batch level. During
|
|
migration, legacy clients that send the same installationId on every
|
|
event remain accepted; conflicting or incomplete identities are rejected.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ProductAnalyticsBatchRequest" }
|
|
responses:
|
|
"200":
|
|
description: Atomic batch acceptance and replay counts
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ProductAnalyticsBatchResponse" }
|
|
"400": { $ref: "#/components/responses/Error" }
|
|
"409": { $ref: "#/components/responses/Error" }
|
|
"422": { $ref: "#/components/responses/Error" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/analytics/keyboard-usage:
|
|
post:
|
|
security:
|
|
- {}
|
|
- bearerAuth: []
|
|
summary: Idempotently accept privacy-minimized daily keyboard usage summaries
|
|
description: |
|
|
Accepts finalized UTC-day counters for text manually committed by
|
|
OSGKeyboard. Language classification happens on-device. Raw text,
|
|
keystrokes, surrounding context, host application identifiers, voice
|
|
transcripts and AI output are never accepted. The current UTC date is
|
|
not accepted because daily summaries are immutable once submitted.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/KeyboardUsageBatchRequest" }
|
|
responses:
|
|
"200":
|
|
description: Atomic batch acceptance and replay counts
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ProductAnalyticsBatchResponse" }
|
|
"400": { $ref: "#/components/responses/Error" }
|
|
"409": { $ref: "#/components/responses/Error" }
|
|
"422": { $ref: "#/components/responses/Error" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/account:
|
|
get:
|
|
summary: Return the account profile
|
|
responses:
|
|
"200":
|
|
description: Account profile
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AccountEnvelope" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
patch:
|
|
summary: Update the current account nickname
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/UpdateAccountProfileRequest" }
|
|
responses:
|
|
"200":
|
|
description: Updated account profile
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AccountEnvelope" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
delete:
|
|
summary: Reauthenticate with Apple, delete the account, and revoke authorization
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/DeleteAccountRequest" }
|
|
responses:
|
|
"204": { description: Account deleted }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/apple/events:
|
|
post:
|
|
security: []
|
|
summary: Receive a signed Apple server-to-server account event
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [payload]
|
|
properties:
|
|
payload: { type: string }
|
|
responses:
|
|
"204": { description: Event accepted }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/credits/balance:
|
|
get:
|
|
summary: Return available and consumed integer credits
|
|
responses:
|
|
"200":
|
|
description: Credit account
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CreditAccount" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/credits/ledger:
|
|
get:
|
|
summary: Return immutable credit history
|
|
parameters:
|
|
- $ref: "#/components/parameters/Limit"
|
|
responses:
|
|
"200":
|
|
description: Ledger entries
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/LedgerEntry" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/credits/rates:
|
|
get:
|
|
summary: Return effective managed-provider rate cards
|
|
responses:
|
|
"200":
|
|
description: Effective rate cards
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { type: object, additionalProperties: true }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/storekit/products:
|
|
get:
|
|
summary: Return enabled consumable credit products
|
|
description: |
|
|
Current catalog: `500tks` grants 500 credits, `1500tks` grants 1,500
|
|
credits, and `3000tks` grants 3,000 credits. Localized prices are
|
|
supplied by StoreKit.
|
|
responses:
|
|
"200":
|
|
description: StoreKit credit product catalog
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/StoreKitProduct" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/storekit/transactions:
|
|
get:
|
|
summary: Return credited StoreKit purchase history for the current account
|
|
description: |
|
|
Returns only App Store transactions that were previously verified and
|
|
committed with their immutable credit-ledger entries. Results include
|
|
purchases credited before this endpoint was introduced and never call
|
|
the App Store at query time. The opaque cursor follows the stable
|
|
descending order of `purchasedAt` and `transactionId`.
|
|
parameters:
|
|
- $ref: "#/components/parameters/Limit"
|
|
- name: cursor
|
|
in: query
|
|
description: Opaque cursor returned by the preceding page.
|
|
schema: { type: string, minLength: 1, maxLength: 256 }
|
|
responses:
|
|
"200":
|
|
description: Credited StoreKit purchases for the authenticated account
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/StoreKitTransactionHistory" }
|
|
"400": { $ref: "#/components/responses/Error" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
post:
|
|
summary: Verify an App Store transaction and idempotently grant credits
|
|
description: |
|
|
Submit the StoreKit 2 `VerificationResult.jwsRepresentation` before
|
|
finishing the consumable transaction. The purchase must include an
|
|
`appAccountToken` equal to the authenticated account UUID. Replaying
|
|
the same App Store transaction returns the original grant.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/StoreKitTransactionRequest" }
|
|
responses:
|
|
"200":
|
|
description: Verified purchase grant or idempotent replay
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/StoreKitPurchase" }
|
|
"409": { $ref: "#/components/responses/Error" }
|
|
"422": { $ref: "#/components/responses/Error" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/referrals:
|
|
get:
|
|
summary: List invitees without exposing their Apple identity
|
|
parameters:
|
|
- $ref: "#/components/parameters/Limit"
|
|
responses:
|
|
"200":
|
|
description: Referral bindings
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: { type: object, additionalProperties: true }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/referrals/me:
|
|
get:
|
|
summary: Return the referral profile and provision its permanent account invite code
|
|
responses:
|
|
"200":
|
|
description: Referral profile
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ReferralProfile" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/referrals/code:
|
|
post:
|
|
deprecated: true
|
|
summary: Compatibility endpoint for the permanent account invite code
|
|
responses:
|
|
"200":
|
|
description: Invite code
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ReferralCode" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/referrals/redeem:
|
|
post:
|
|
summary: Bind the account to an inviter during the eligibility window
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/RedeemReferralRequest" }
|
|
responses:
|
|
"200": { description: Referral binding }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/referrals/bind:
|
|
post:
|
|
deprecated: true
|
|
summary: Compatibility alias for referral redemption
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/RedeemReferralRequest" }
|
|
responses:
|
|
"200": { description: Referral binding }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/referrals/campaigns:
|
|
get:
|
|
summary: Return active referral campaigns
|
|
responses:
|
|
"200": { description: Active campaigns }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/integrity/challenges:
|
|
post:
|
|
security: []
|
|
summary: Issue a single-use App Attest challenge
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AppAttestChallengeRequest" }
|
|
responses:
|
|
"201":
|
|
description: Challenge issued
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AppAttestChallengeResponse" }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/integrity/attest:
|
|
post:
|
|
security: []
|
|
summary: Register a validated App Attest key
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AppAttestationRequest" }
|
|
responses:
|
|
"204": { description: Key registered }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/integrity/assert:
|
|
post:
|
|
security: []
|
|
summary: Validate a challenge-only App Attest assertion
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AppAssertionRequest" }
|
|
responses:
|
|
"200": { description: Assertion counter advanced }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/oobe/grants:
|
|
post:
|
|
security: []
|
|
summary: Create a short-lived anonymous OOBE gateway grant
|
|
description: |
|
|
Verifies an App Attest assertion bound to the installation and returns
|
|
credentials limited to the four one-time onboarding AI features.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CreateOobeGrantRequest" }
|
|
responses:
|
|
"201":
|
|
description: OOBE gateway credentials
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OobeGrantTokens" }
|
|
default: { $ref: "#/components/responses/GatewayError" }
|
|
/v1/oobe/grants/refresh:
|
|
post:
|
|
security: []
|
|
summary: Rotate an anonymous OOBE refresh token
|
|
parameters:
|
|
- $ref: "#/components/parameters/IdempotencyKey"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/RefreshOobeGrantRequest" }
|
|
responses:
|
|
"200":
|
|
description: Rotated OOBE gateway credentials
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OobeGrantTokens" }
|
|
default: { $ref: "#/components/responses/GatewayError" }
|
|
/v1/gateway/catalog:
|
|
get:
|
|
summary: Return configured managed-provider capabilities
|
|
responses:
|
|
"200": { description: Provider catalog }
|
|
default: { $ref: "#/components/responses/Error" }
|
|
/v1/gateway/grants:
|
|
post:
|
|
summary: Create an idempotent managed-provider grant
|
|
parameters:
|
|
- $ref: "#/components/parameters/IdempotencyKey"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CreateGatewayGrantRequest" }
|
|
responses:
|
|
"201":
|
|
description: Gateway grant and rotating credentials
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/GatewayGrantTokens" }
|
|
default: { $ref: "#/components/responses/GatewayError" }
|
|
/v1/gateway/grants/refresh:
|
|
post:
|
|
security: []
|
|
summary: Rotate a gateway refresh token
|
|
parameters:
|
|
- $ref: "#/components/parameters/IdempotencyKey"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/RefreshGatewayGrantRequest" }
|
|
responses:
|
|
"200":
|
|
description: Rotated gateway credentials
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/GatewayGrantTokens" }
|
|
default: { $ref: "#/components/responses/GatewayError" }
|
|
/v1/gateway/grants/{grantId}:
|
|
delete:
|
|
summary: Revoke a gateway grant
|
|
parameters:
|
|
- name: grantId
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
responses:
|
|
"204": { description: Gateway grant revoked }
|
|
default: { $ref: "#/components/responses/GatewayError" }
|
|
/v1/gateway/llm/{capability}:
|
|
post:
|
|
summary: Run a metered polish, AI, or agent request
|
|
description: |
|
|
The server deterministically selects model, thinking, search, tools, retry,
|
|
and output-budget policy from `capability` plus optional `taskKind`. It
|
|
never infers task type from `input` or `context`, and clients cannot
|
|
supply provider parameters. Search and tools are currently disabled.
|
|
For account grants, `oobe` is accepted only for dictation polish and the
|
|
first successful request per account is complimentary. Anonymous OOBE
|
|
grants require a matching `oobeFeature` and allow one successful request
|
|
per feature. Later attempts fail without falling through to paid billing.
|
|
parameters:
|
|
- $ref: "#/components/parameters/RequestId"
|
|
- name: capability
|
|
in: path
|
|
required: true
|
|
schema: { type: string, enum: [polish, ai, agent] }
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/TextGatewayRequest" }
|
|
responses:
|
|
"200":
|
|
description: JSON response, or SSE when stream is true
|
|
default: { $ref: "#/components/responses/GatewayError" }
|
|
/v1/gateway/asr:
|
|
post:
|
|
summary: Run buffered metered ASR
|
|
parameters:
|
|
- $ref: "#/components/parameters/RequestId"
|
|
- { name: X-Audio-Duration-Ms, in: header, required: true, schema: { type: integer, minimum: 1, maximum: 600000 } }
|
|
- { name: X-Audio-Format, in: header, schema: { type: string, enum: [pcm, wav, ogg, mp3], default: pcm } }
|
|
- { name: X-Audio-Codec, in: header, schema: { type: string, enum: [raw, opus], default: raw } }
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/octet-stream:
|
|
schema: { type: string, contentEncoding: binary }
|
|
responses:
|
|
"200": { description: Newline-delimited Volcengine ASR result frames }
|
|
default: { $ref: "#/components/responses/GatewayError" }
|
|
/v1/gateway/asr/sessions:
|
|
post:
|
|
summary: Reserve credits and create a one-shot streaming ASR session
|
|
parameters:
|
|
- $ref: "#/components/parameters/RequestId"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CreateAsrSessionRequest" }
|
|
responses:
|
|
"201":
|
|
description: Session created
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CreateAsrSessionResponse" }
|
|
default: { $ref: "#/components/responses/GatewayError" }
|
|
/v1/gateway/asr/sessions/{sessionId}/stream:
|
|
get:
|
|
summary: Upgrade to WebSocket and stream binary audio frames
|
|
description: |
|
|
Send authenticated binary audio frames, then the exact text frame
|
|
`{"type":"end"}`. The server forwards binary provider-result frames and
|
|
closes the one-shot session after settlement.
|
|
parameters:
|
|
- name: sessionId
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
responses:
|
|
"101": { description: WebSocket upgrade }
|
|
default: { $ref: "#/components/responses/GatewayError" }
|
|
/v1/content/skills:
|
|
get:
|
|
security: []
|
|
summary: Return the enabled official Skill catalog
|
|
parameters:
|
|
- name: If-None-Match
|
|
in: header
|
|
schema: { type: string }
|
|
responses:
|
|
"200":
|
|
description: Versioned official Skill catalog
|
|
headers:
|
|
ETag: { schema: { type: string } }
|
|
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/OfficialSkillCatalog" }
|
|
"304": { description: The caller already has the current revision }
|
|
/v1/content/hints/manifest:
|
|
get:
|
|
security: []
|
|
summary: Return the published AI Hint pack manifest
|
|
parameters:
|
|
- name: If-None-Match
|
|
in: header
|
|
schema: { type: string }
|
|
responses:
|
|
"200":
|
|
description: Published locale manifest
|
|
headers:
|
|
ETag: { schema: { type: string } }
|
|
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AIHintManifest" }
|
|
"304": { description: The caller already has the current manifest }
|
|
/v1/content/hints/{locale}:
|
|
get:
|
|
security: []
|
|
summary: Return a published AI Hint pack
|
|
parameters:
|
|
- $ref: "#/components/parameters/HintLocale"
|
|
- name: If-None-Match
|
|
in: header
|
|
schema: { type: string }
|
|
responses:
|
|
"200":
|
|
description: Published AI Hint pack
|
|
headers:
|
|
ETag: { schema: { type: string } }
|
|
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AIHintPack" }
|
|
"304": { description: The caller already has this pack version }
|
|
"404": { description: Locale is unsupported or has not been published }
|
|
/hints/manifest.json:
|
|
get:
|
|
security: []
|
|
summary: Return the AI Hint manifest at the legacy file path
|
|
parameters:
|
|
- name: If-None-Match
|
|
in: header
|
|
schema: { type: string }
|
|
responses:
|
|
"200":
|
|
description: Same payload and cache validators as /v1/content/hints/manifest
|
|
headers:
|
|
ETag: { schema: { type: string } }
|
|
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AIHintManifest" }
|
|
"304": { description: The caller already has the current manifest }
|
|
/hints/hints-{locale}.json:
|
|
get:
|
|
security: []
|
|
summary: Return an AI Hint pack at the legacy file path
|
|
parameters:
|
|
- $ref: "#/components/parameters/HintLocale"
|
|
- name: If-None-Match
|
|
in: header
|
|
schema: { type: string }
|
|
responses:
|
|
"200":
|
|
description: Same payload and cache validators as /v1/content/hints/{locale}
|
|
headers:
|
|
ETag: { schema: { type: string } }
|
|
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AIHintPack" }
|
|
"304": { description: The caller already has this pack version }
|
|
"404": { description: Locale is unsupported or has not been published }
|
|
/.well-known/apple-app-site-association:
|
|
get:
|
|
servers:
|
|
- url: https://osglab.com
|
|
security: []
|
|
summary: Return the Apple Universal Links association document
|
|
responses:
|
|
"200":
|
|
description: AASA JSON without a redirect
|
|
headers:
|
|
Cache-Control:
|
|
schema: { type: string, const: "no-store, max-age=0" }
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AppleAppSiteAssociation" }
|
|
/apple-app-site-association:
|
|
get:
|
|
servers:
|
|
- url: https://osglab.com
|
|
security: []
|
|
summary: Return the root compatibility AASA document
|
|
responses:
|
|
"200":
|
|
description: AASA JSON without a redirect
|
|
headers:
|
|
Cache-Control:
|
|
schema: { type: string, const: "no-store, max-age=0" }
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AppleAppSiteAssociation" }
|
|
/i/{code}:
|
|
get:
|
|
servers:
|
|
- url: https://osglab.com
|
|
security: []
|
|
summary: Privacy-preserving invitation landing page
|
|
parameters:
|
|
- name: code
|
|
in: path
|
|
required: true
|
|
schema: { type: string, pattern: "^[A-Za-z0-9_-]{22}$" }
|
|
responses:
|
|
"200": { description: Bilingual HTML landing page }
|
|
"404": { description: Invalid, unknown, or expired invitation }
|
|
"503": { description: Invitation lookup is temporarily unavailable }
|
|
/v1/admin/content/skills:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: List all official Skills including disabled entries
|
|
responses:
|
|
"200":
|
|
description: Administrative Skill catalog
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminOfficialSkillCatalog" }
|
|
"403": { description: SUPER_ADMIN or SUPPORT role is required }
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Create a disabled official Skill
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/CreateOfficialSkillRequest" }
|
|
responses:
|
|
"201":
|
|
description: Skill created
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminOfficialSkill" }
|
|
"400": { description: Request is invalid }
|
|
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
|
"409": { description: Skill ID already exists }
|
|
/v1/admin/content/skills/{id}:
|
|
put:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Update an official Skill and increment catalog revision
|
|
parameters:
|
|
- $ref: "#/components/parameters/OfficialSkillId"
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/UpdateOfficialSkillRequest" }
|
|
responses:
|
|
"200":
|
|
description: Skill updated
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminOfficialSkill" }
|
|
"400": { description: Request is invalid }
|
|
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
|
"404": { description: Skill was not found }
|
|
/v1/admin/content/skills/{id}/enable:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Enable an official Skill and increment catalog revision
|
|
parameters:
|
|
- $ref: "#/components/parameters/OfficialSkillId"
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
responses:
|
|
"204": { description: Skill enabled }
|
|
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
|
"404": { description: Skill was not found }
|
|
/v1/admin/content/skills/{id}/disable:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Disable an official Skill and increment catalog revision
|
|
parameters:
|
|
- $ref: "#/components/parameters/OfficialSkillId"
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
responses:
|
|
"204": { description: Skill disabled }
|
|
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
|
"404": { description: Skill was not found }
|
|
/v1/admin/content/hints/generation/settings:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return non-secret AI Hint generation settings
|
|
responses:
|
|
"200":
|
|
description: Current generation settings and secret availability flags
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/HintFeedSettings" }
|
|
"403": { description: SUPER_ADMIN or SUPPORT role is required }
|
|
put:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Update non-secret AI Hint generation settings
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/UpdateHintFeedSettingsRequest" }
|
|
responses:
|
|
"200":
|
|
description: Updated generation settings
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/HintFeedSettings" }
|
|
"400": { description: Settings are invalid }
|
|
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
|
/v1/admin/content/hints/generation/status:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return AI Hint generation and scheduler status
|
|
responses:
|
|
"200":
|
|
description: Durable generation status and current pack versions
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/HintFeedGenerationStatus" }
|
|
"403": { description: SUPER_ADMIN or SUPPORT role is required }
|
|
/v1/admin/content/hints/generation/regenerate:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Generate and atomically publish both AI Hint locale packs
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
responses:
|
|
"200":
|
|
description: Both locale packs were generated and published
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/HintFeedGenerationResponse" }
|
|
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
|
"409": { description: A generation is already in progress }
|
|
"502": { description: Generation failed and the previous packs remain published }
|
|
/v1/admin/content/hints/{locale}:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return a locale Hint pack for editing
|
|
parameters:
|
|
- $ref: "#/components/parameters/HintLocale"
|
|
responses:
|
|
"200":
|
|
description: Existing pack or an empty version-zero editor document
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminAIHintPack" }
|
|
"403": { description: SUPER_ADMIN or SUPPORT role is required }
|
|
put:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Save and immediately apply edits to a locale Hint pack
|
|
parameters:
|
|
- $ref: "#/components/parameters/HintLocale"
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/UpdateAIHintPackRequest" }
|
|
responses:
|
|
"200":
|
|
description: Saved active pack
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminAIHintPack" }
|
|
"400": { description: Pack is invalid }
|
|
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
|
/v1/admin/auth/session:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
summary: Check the current administrator session
|
|
responses:
|
|
"200":
|
|
description: Authenticated or anonymous session state
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminSessionState" }
|
|
"404": { description: Verified administrator client certificate is absent while mTLS is required }
|
|
/v1/admin/auth/login:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
summary: Authenticate an administrator with password and TOTP
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [username, password, totpCode]
|
|
properties:
|
|
username: { type: string, minLength: 3, maxLength: 64 }
|
|
password: { type: string, minLength: 1, maxLength: 1024 }
|
|
totpCode: { type: string, pattern: "^[0-9]{6}$" }
|
|
responses:
|
|
"200":
|
|
description: Secure session and CSRF cookies created
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminLoginResponse" }
|
|
"401": { description: Credentials are invalid }
|
|
"429": { description: Login is locked or rate limited }
|
|
/v1/admin/auth/logout:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Revoke the current administrator session
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
responses:
|
|
"204": { description: Session revoked }
|
|
"401": { description: Session is invalid }
|
|
/v1/admin/overview:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return registration, activity, and credit overview statistics
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminRange"
|
|
responses:
|
|
"200":
|
|
description: Overview statistics
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminOverview" }
|
|
"400": { description: Range is invalid }
|
|
"401": { description: Session is invalid }
|
|
/v1/admin/referrals:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return referral funnel and ranking statistics
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminRange"
|
|
- name: sort
|
|
in: query
|
|
schema: { type: string, enum: [invited, qualified, creditsEarned] }
|
|
- $ref: "#/components/parameters/AdminSortOrder"
|
|
- name: limit
|
|
in: query
|
|
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
|
|
responses:
|
|
"200":
|
|
description: Referral statistics
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminReferralOverview" }
|
|
"400": { description: Range, sort, order, or limit is invalid }
|
|
"401": { description: Session is invalid }
|
|
/v1/admin/analytics:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return product growth, retention, usage and monetization analytics
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminRange"
|
|
responses:
|
|
"200":
|
|
description: Privacy-minimized product analytics aggregates
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminProductAnalytics" }
|
|
"400": { description: Range is invalid }
|
|
"401": { description: Session is invalid }
|
|
/v1/admin/users:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: List users or search by full or 8-character internal user ID suffix
|
|
parameters:
|
|
- name: q
|
|
in: query
|
|
schema: { type: string, maxLength: 36 }
|
|
- name: cursor
|
|
in: query
|
|
schema: { type: string, maxLength: 256 }
|
|
- $ref: "#/components/parameters/Limit"
|
|
- $ref: "#/components/parameters/AdminFrom"
|
|
- $ref: "#/components/parameters/AdminUntil"
|
|
- name: status
|
|
in: query
|
|
schema: { type: string, enum: [active, suspended] }
|
|
- $ref: "#/components/parameters/AdminCreatedAtSort"
|
|
- $ref: "#/components/parameters/AdminSortOrder"
|
|
responses:
|
|
"200":
|
|
description: Privacy-minimized user summaries
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminUserPage" }
|
|
"400": { description: Query, filter, sort, limit, or cursor is malformed }
|
|
"401": { description: Session is invalid }
|
|
"403": { description: ANALYST role cannot access user records }
|
|
/v1/admin/users/{userId}:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return privacy-minimized user details
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminUserId"
|
|
responses:
|
|
"200":
|
|
description: User details
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminUserDetail" }
|
|
"403": { description: ANALYST role cannot access user records }
|
|
"404": { description: User was not found }
|
|
/v1/admin/users/{userId}/ledger:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return the immutable credit ledger for a user
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminUserId"
|
|
- name: cursor
|
|
in: query
|
|
schema: { type: string, maxLength: 256 }
|
|
- $ref: "#/components/parameters/AdminLedgerLimit"
|
|
- $ref: "#/components/parameters/AdminFrom"
|
|
- $ref: "#/components/parameters/AdminUntil"
|
|
- $ref: "#/components/parameters/AdminLedgerType"
|
|
- $ref: "#/components/parameters/AdminLedgerEntryType"
|
|
- $ref: "#/components/parameters/AdminLedgerUsageType"
|
|
- $ref: "#/components/parameters/AdminLedgerReferenceId"
|
|
- $ref: "#/components/parameters/AdminLedgerSort"
|
|
- $ref: "#/components/parameters/AdminSortOrder"
|
|
responses:
|
|
"200":
|
|
description: Credit ledger entries ordered by the requested field and unique entry ID
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminLedgerPage" }
|
|
"400": { description: Filter, sort, limit, or cursor is malformed }
|
|
"403": { description: ANALYST role cannot access credit ledger records }
|
|
"404": { description: User was not found }
|
|
/v1/admin/credits/ledger:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return the latest immutable credit ledger entries across users
|
|
parameters:
|
|
- name: cursor
|
|
in: query
|
|
schema: { type: string, maxLength: 256 }
|
|
- $ref: "#/components/parameters/AdminLedgerLimit"
|
|
- $ref: "#/components/parameters/AdminFrom"
|
|
- $ref: "#/components/parameters/AdminUntil"
|
|
- $ref: "#/components/parameters/AdminLedgerType"
|
|
- $ref: "#/components/parameters/AdminLedgerEntryType"
|
|
- $ref: "#/components/parameters/AdminLedgerUsageType"
|
|
- $ref: "#/components/parameters/AdminLedgerReferenceId"
|
|
- $ref: "#/components/parameters/AdminLedgerSort"
|
|
- $ref: "#/components/parameters/AdminSortOrder"
|
|
responses:
|
|
"200":
|
|
description: Latest credit ledger entries ordered by the requested field and unique entry ID
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminLedgerPage" }
|
|
"400": { description: Filter, sort, limit, or cursor is malformed }
|
|
"403": { description: ANALYST role cannot access credit ledger records }
|
|
/v1/admin/credits/grants:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Grant integer credits through an idempotent ledger transaction
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
- $ref: "#/components/parameters/IdempotencyKey"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [userId, amount, reason]
|
|
properties:
|
|
userId: { type: string, format: uuid }
|
|
amount: { type: integer, format: int64, minimum: 1 }
|
|
reason: { type: string, minLength: 4, maxLength: 200 }
|
|
responses:
|
|
"200":
|
|
description: Grant applied or replayed
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminGrantResponse" }
|
|
"400": { description: Grant input or idempotency key is invalid }
|
|
"403": { description: CSRF or role authorization failed }
|
|
"404": { description: Target user was not found }
|
|
"409": { description: Idempotency key conflicts with another grant }
|
|
/v1/admin/operators/summary:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return administrator and active-session security indicators
|
|
responses:
|
|
"200":
|
|
description: Security indicators
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [enabledOperators, lockedOperators, activeSessions]
|
|
properties:
|
|
enabledOperators: { type: integer, minimum: 0 }
|
|
lockedOperators: { type: integer, minimum: 0 }
|
|
activeSessions: { type: integer, format: int64, minimum: 0 }
|
|
"403": { description: INSUFFICIENT_PERMISSION; SUPER_ADMIN is required }
|
|
/v1/admin/operators:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: List administrator operators
|
|
parameters:
|
|
- name: cursor
|
|
in: query
|
|
schema: { type: string, maxLength: 256 }
|
|
- $ref: "#/components/parameters/Limit"
|
|
- $ref: "#/components/parameters/AdminFrom"
|
|
- $ref: "#/components/parameters/AdminUntil"
|
|
- name: role
|
|
in: query
|
|
schema: { type: string, enum: [SUPER_ADMIN, SUPPORT, ANALYST] }
|
|
- name: enabled
|
|
in: query
|
|
schema: { type: boolean }
|
|
- name: locked
|
|
in: query
|
|
description: Whether locked_until is later than the request time.
|
|
schema: { type: boolean }
|
|
- name: sort
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum: [createdAt, username, lastLoginAt]
|
|
default: createdAt
|
|
- $ref: "#/components/parameters/AdminSortOrder"
|
|
responses:
|
|
"200":
|
|
description: Operators ordered by the requested stable sort and operator ID; null lastLoginAt values are last
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminOperatorPage" }
|
|
"403": { description: INSUFFICIENT_PERMISSION; SUPER_ADMIN is required }
|
|
"400": { description: Filter, sort, limit, or cursor is malformed }
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Create an operator and return TOTP provisioning data once
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminOperatorCreateRequest" }
|
|
responses:
|
|
"201":
|
|
description: Operator created; plaintext TOTP material is returned only here
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminOperatorProvisioning" }
|
|
"400": { description: VALIDATION_ERROR }
|
|
"403": { description: CSRF_INVALID, ORIGIN_INVALID, or INSUFFICIENT_PERMISSION }
|
|
"409": { description: ADMIN_USERNAME_CONFLICT }
|
|
/v1/admin/operators/{operatorId}/enable:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Enable an operator
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminOperatorId"
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
responses:
|
|
"204": { description: Operator enabled and action audited }
|
|
"403": { description: CSRF_INVALID, ORIGIN_INVALID, or INSUFFICIENT_PERMISSION }
|
|
"404": { description: ADMIN_OPERATOR_NOT_FOUND }
|
|
/v1/admin/operators/{operatorId}/disable:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Disable an operator and atomically revoke all active sessions
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminOperatorId"
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
responses:
|
|
"204": { description: Operator disabled and sessions revoked }
|
|
"403": { description: CSRF_INVALID, ORIGIN_INVALID, or INSUFFICIENT_PERMISSION }
|
|
"404": { description: ADMIN_OPERATOR_NOT_FOUND }
|
|
"409": { description: CANNOT_DISABLE_SELF or LAST_SUPER_ADMIN_REQUIRED }
|
|
/v1/admin/operators/{operatorId}/unlock:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Clear an operator login lock
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminOperatorId"
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
responses:
|
|
"204": { description: Operator unlocked }
|
|
"403": { description: CSRF_INVALID, ORIGIN_INVALID, or INSUFFICIENT_PERMISSION }
|
|
"404": { description: ADMIN_OPERATOR_NOT_FOUND }
|
|
/v1/admin/operators/{operatorId}/credentials/reset:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Reset password and TOTP, atomically revoking all active sessions
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminOperatorId"
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminOperatorPasswordRequest" }
|
|
responses:
|
|
"200":
|
|
description: Credentials reset; plaintext TOTP material is returned only here
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminOperatorProvisioning" }
|
|
"400": { description: VALIDATION_ERROR }
|
|
"403": { description: CSRF_INVALID, ORIGIN_INVALID, or INSUFFICIENT_PERMISSION }
|
|
"404": { description: ADMIN_OPERATOR_NOT_FOUND }
|
|
/v1/admin/operators/{operatorId}/sessions/revoke:
|
|
post:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Revoke every active session for an operator
|
|
parameters:
|
|
- $ref: "#/components/parameters/AdminOperatorId"
|
|
- $ref: "#/components/parameters/AdminCsrf"
|
|
responses:
|
|
"204": { description: All active sessions revoked }
|
|
"403": { description: CSRF_INVALID, ORIGIN_INVALID, or INSUFFICIENT_PERMISSION }
|
|
"404": { description: ADMIN_OPERATOR_NOT_FOUND }
|
|
/v1/admin/audit:
|
|
get:
|
|
security:
|
|
- adminMtls: []
|
|
adminSession: []
|
|
summary: Return append-only administrator audit events
|
|
parameters:
|
|
- name: cursor
|
|
in: query
|
|
schema: { type: string, maxLength: 256 }
|
|
- $ref: "#/components/parameters/Limit"
|
|
- $ref: "#/components/parameters/AdminFrom"
|
|
- $ref: "#/components/parameters/AdminUntil"
|
|
- name: action
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- LOGIN_SUCCEEDED
|
|
- LOGIN_FAILED
|
|
- SESSION_REVOKED
|
|
- OPERATOR_CREATED
|
|
- OPERATOR_ENABLED
|
|
- OPERATOR_DISABLED
|
|
- OPERATOR_UNLOCKED
|
|
- OPERATOR_CREDENTIALS_RESET
|
|
- OPERATOR_SESSIONS_REVOKED
|
|
- MANUAL_CREDIT_GRANTED
|
|
- CONTENT_SKILL_CREATED
|
|
- CONTENT_SKILL_UPDATED
|
|
- CONTENT_SKILL_ENABLED
|
|
- CONTENT_SKILL_DISABLED
|
|
- CONTENT_HINT_PACK_PUBLISHED
|
|
- CONTENT_HINT_PACK_SAVED
|
|
- CONTENT_HINT_FEED_SETTINGS_UPDATED
|
|
- CONTENT_HINT_FEED_GENERATED
|
|
- name: result
|
|
in: query
|
|
schema: { type: string, enum: [success, rejected] }
|
|
- $ref: "#/components/parameters/AdminCreatedAtSort"
|
|
- $ref: "#/components/parameters/AdminSortOrder"
|
|
responses:
|
|
"200":
|
|
description: Recent audit events
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/AdminAuditPage" }
|
|
"400": { description: Filter, sort, limit, or cursor is malformed }
|
|
"403": { description: Super-administrator role is required }
|
|
components:
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
adminMtls:
|
|
type: mutualTLS
|
|
description: Client certificate issued by the dedicated administrator CA; required when ADMIN_MTLS_REQUIRED is true.
|
|
adminSession:
|
|
type: apiKey
|
|
in: cookie
|
|
name: osg_admin_session
|
|
parameters:
|
|
Limit:
|
|
name: limit
|
|
in: query
|
|
schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
|
|
RequestId:
|
|
name: X-Request-ID
|
|
in: header
|
|
required: true
|
|
schema: { type: string, pattern: "^[A-Za-z0-9_-]{8,64}$" }
|
|
IdempotencyKey:
|
|
name: Idempotency-Key
|
|
in: header
|
|
required: true
|
|
schema: { type: string, minLength: 8, maxLength: 128 }
|
|
OfficialSkillId:
|
|
name: id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
maxLength: 100
|
|
pattern: "^official\\.[a-z0-9._-]+$"
|
|
HintLocale:
|
|
name: locale
|
|
in: path
|
|
required: true
|
|
schema: { type: string, enum: [zh, en] }
|
|
AdminCsrf:
|
|
name: X-CSRF-Token
|
|
in: header
|
|
required: true
|
|
schema: { type: string, minLength: 32, maxLength: 512 }
|
|
AdminRange:
|
|
name: range
|
|
in: query
|
|
description: Covers exactly 7, 30, or 90 UTC calendar dates, from 00:00 on the first date through the current instant. The current UTC date is partial.
|
|
schema: { type: string, enum: [7d, 30d, 90d], default: 30d }
|
|
AdminFrom:
|
|
name: from
|
|
in: query
|
|
description: Inclusive UTC lower bound. When both bounds are present, from must be earlier than until.
|
|
schema: { type: string, format: date-time }
|
|
AdminUntil:
|
|
name: until
|
|
in: query
|
|
description: Exclusive UTC upper bound.
|
|
schema: { type: string, format: date-time }
|
|
AdminCreatedAtSort:
|
|
name: sort
|
|
in: query
|
|
schema: { type: string, enum: [createdAt], default: createdAt }
|
|
AdminSortOrder:
|
|
name: order
|
|
in: query
|
|
schema: { type: string, enum: [asc, desc] }
|
|
AdminLedgerType:
|
|
name: type
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum: [grant, reserve, settle, refund]
|
|
AdminLedgerEntryType:
|
|
name: entryType
|
|
in: query
|
|
description: Exact immutable ledger entry type. This filter is ANDed with all other filters.
|
|
schema:
|
|
$ref: "#/components/schemas/LedgerEntryType"
|
|
AdminLedgerUsageType:
|
|
name: usageType
|
|
in: query
|
|
description: Product usage associated with the reservation. HOTWORD request source takes precedence over capability.
|
|
schema: { type: string, enum: [polish, asr, ai, agent, hotword] }
|
|
AdminLedgerReferenceId:
|
|
name: referenceId
|
|
in: query
|
|
schema: { type: string, format: uuid }
|
|
AdminLedgerSort:
|
|
name: sort
|
|
in: query
|
|
schema: { type: string, enum: [createdAt, amount], default: createdAt }
|
|
AdminLedgerLimit:
|
|
name: limit
|
|
in: query
|
|
schema: { type: integer, minimum: 1, maximum: 100, default: 100 }
|
|
AdminUserId:
|
|
name: userId
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
AdminOperatorId:
|
|
name: operatorId
|
|
in: path
|
|
required: true
|
|
schema: { type: string, format: uuid }
|
|
responses:
|
|
HealthUp:
|
|
description: Service is healthy
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [status]
|
|
properties:
|
|
status: { type: string, const: UP }
|
|
Error:
|
|
description: API error
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/ApiErrorEnvelope" }
|
|
GatewayError:
|
|
description: Managed-provider error
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/GatewayError" }
|
|
schemas:
|
|
ProductAnalyticsEvent:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [clientEventId, eventType, occurredAt, surface]
|
|
properties:
|
|
clientEventId: { type: string, format: uuid }
|
|
eventType:
|
|
type: string
|
|
enum:
|
|
- FIRST_OPEN
|
|
- SESSION_STARTED
|
|
- KEYBOARD_ACTIVATED
|
|
- AI_FEATURE_STARTED
|
|
- AI_FEATURE_SUCCEEDED
|
|
- AI_FEATURE_FAILED
|
|
- PURCHASE_VIEWED
|
|
- PURCHASE_STARTED
|
|
- PURCHASE_CANCELLED
|
|
- REFERRAL_SHARED
|
|
- INVITE_OPENED
|
|
occurredAt: { type: string, format: date-time }
|
|
surface: { type: string, enum: [APP, KEYBOARD, INVITE_WEB] }
|
|
acquisitionChannel:
|
|
type: string
|
|
enum: [APP_STORE_ORGANIC, REFERRAL, SOCIAL_CONTENT, UNKNOWN]
|
|
feature:
|
|
type: string
|
|
enum: [TRANSCRIPTION, POLISH, AI_ASSISTANT, AGENT, HOTWORD, OTHER]
|
|
executionMode: { type: string, enum: [MANAGED, LOCAL, BYOK] }
|
|
failureCategory:
|
|
type: string
|
|
enum: [NETWORK, PROVIDER, TIMEOUT, CANCELLED, INSUFFICIENT_CREDITS, VALIDATION, UNKNOWN]
|
|
durationBucket:
|
|
type: string
|
|
enum: [LT_1S, S1_TO_3, S3_TO_10, S10_TO_30, GTE_30S]
|
|
appVersion:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 32
|
|
pattern: "^[A-Za-z0-9._+-]+$"
|
|
osVersion:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 32
|
|
pattern: "^[A-Za-z0-9._+-]+$"
|
|
installationId:
|
|
type: string
|
|
format: uuid
|
|
deprecated: true
|
|
description: Transitional legacy field; new clients must use the batch-level installationId.
|
|
ProductAnalyticsBatchRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [events]
|
|
properties:
|
|
installationId:
|
|
type: string
|
|
format: uuid
|
|
description: Required for new clients; legacy batches may instead repeat one identical ID on every event.
|
|
events:
|
|
type: array
|
|
minItems: 1
|
|
maxItems: 50
|
|
items: { $ref: "#/components/schemas/ProductAnalyticsEvent" }
|
|
KeyboardUsageSummary:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- clientSummaryId
|
|
- summaryDate
|
|
- chineseCharacterCount
|
|
- englishCharacterCount
|
|
- otherCharacterCount
|
|
- inputSessionCount
|
|
- chineseOnlySessionCount
|
|
- englishOnlySessionCount
|
|
- mixedLanguageSessionCount
|
|
- otherOnlySessionCount
|
|
properties:
|
|
clientSummaryId: { type: string, format: uuid }
|
|
summaryDate:
|
|
type: string
|
|
format: date
|
|
description: Finalized UTC date; accepted from 35 days ago through yesterday.
|
|
chineseCharacterCount: { type: integer, format: int64, minimum: 0, maximum: 1000000 }
|
|
englishCharacterCount: { type: integer, format: int64, minimum: 0, maximum: 1000000 }
|
|
otherCharacterCount: { type: integer, format: int64, minimum: 0, maximum: 1000000 }
|
|
inputSessionCount: { type: integer, format: int64, minimum: 1, maximum: 100000 }
|
|
chineseOnlySessionCount: { type: integer, format: int64, minimum: 0, maximum: 100000 }
|
|
englishOnlySessionCount: { type: integer, format: int64, minimum: 0, maximum: 100000 }
|
|
mixedLanguageSessionCount: { type: integer, format: int64, minimum: 0, maximum: 100000 }
|
|
otherOnlySessionCount: { type: integer, format: int64, minimum: 0, maximum: 100000 }
|
|
appVersion:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 32
|
|
pattern: "^[A-Za-z0-9._+-]+$"
|
|
osVersion:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 32
|
|
pattern: "^[A-Za-z0-9._+-]+$"
|
|
KeyboardUsageBatchRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [installationId, summaries]
|
|
properties:
|
|
installationId: { type: string, format: uuid }
|
|
summaries:
|
|
type: array
|
|
minItems: 1
|
|
maxItems: 50
|
|
items: { $ref: "#/components/schemas/KeyboardUsageSummary" }
|
|
ProductAnalyticsBatchResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [accepted, replayed]
|
|
properties:
|
|
accepted: { type: integer, minimum: 0, maximum: 50 }
|
|
replayed: { type: integer, minimum: 0, maximum: 50 }
|
|
SkillLocalization:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [name, summary, prompt]
|
|
properties:
|
|
name: { type: string, minLength: 1, maxLength: 40 }
|
|
summary: { type: string, minLength: 1, maxLength: 200 }
|
|
prompt: { type: string, minLength: 1, maxLength: 6000 }
|
|
SkillLocalizations:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [zh-Hans, en]
|
|
properties:
|
|
zh-Hans: { $ref: "#/components/schemas/SkillLocalization" }
|
|
en: { $ref: "#/components/schemas/SkillLocalization" }
|
|
OfficialSkill:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [id, systemImage, sortOrder, kind, thinkingEnabled, localizations]
|
|
properties:
|
|
id:
|
|
type: string
|
|
maxLength: 100
|
|
pattern: "^official\\.[a-z0-9._-]+$"
|
|
systemImage: { type: string, minLength: 1, maxLength: 100 }
|
|
sortOrder: { type: integer, minimum: 0, maximum: 100000 }
|
|
kind: { type: string, const: transform }
|
|
thinkingEnabled: { type: boolean }
|
|
localizations: { $ref: "#/components/schemas/SkillLocalizations" }
|
|
AdminOfficialSkill:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [id, systemImage, sortOrder, kind, thinkingEnabled, enabled, localizations]
|
|
properties:
|
|
id:
|
|
type: string
|
|
maxLength: 100
|
|
pattern: "^official\\.[a-z0-9._-]+$"
|
|
systemImage: { type: string, minLength: 1, maxLength: 100 }
|
|
sortOrder: { type: integer, minimum: 0, maximum: 100000 }
|
|
kind: { type: string, const: transform }
|
|
thinkingEnabled: { type: boolean }
|
|
enabled: { type: boolean }
|
|
localizations: { $ref: "#/components/schemas/SkillLocalizations" }
|
|
OfficialSkillCatalog:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [schemaVersion, revision, skills]
|
|
properties:
|
|
schemaVersion: { type: integer, const: 1 }
|
|
revision: { type: integer, format: int64, minimum: 0 }
|
|
generatedAt: { type: string, format: date-time }
|
|
skills:
|
|
type: array
|
|
maxItems: 100
|
|
items: { $ref: "#/components/schemas/OfficialSkill" }
|
|
AdminOfficialSkillCatalog:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [revision, skills]
|
|
properties:
|
|
revision: { type: integer, format: int64, minimum: 0 }
|
|
generatedAt: { type: string, format: date-time }
|
|
skills:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminOfficialSkill" }
|
|
CreateOfficialSkillRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [id, systemImage, sortOrder, thinkingEnabled, localizations]
|
|
properties:
|
|
id:
|
|
type: string
|
|
maxLength: 100
|
|
pattern: "^official\\.[a-z0-9._-]+$"
|
|
systemImage: { type: string, minLength: 1, maxLength: 100 }
|
|
sortOrder: { type: integer, minimum: 0, maximum: 100000 }
|
|
thinkingEnabled: { type: boolean }
|
|
localizations: { $ref: "#/components/schemas/SkillLocalizations" }
|
|
UpdateOfficialSkillRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [systemImage, sortOrder, thinkingEnabled, localizations]
|
|
properties:
|
|
systemImage: { type: string, minLength: 1, maxLength: 100 }
|
|
sortOrder: { type: integer, minimum: 0, maximum: 100000 }
|
|
thinkingEnabled: { type: boolean }
|
|
localizations: { $ref: "#/components/schemas/SkillLocalizations" }
|
|
AIHintCard:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [id, prompt, category, priority, source, locale, conditions]
|
|
anyOf:
|
|
- required: [displayText]
|
|
- required: [text]
|
|
properties:
|
|
id: { type: string, minLength: 1, maxLength: 128 }
|
|
displayText: { type: string, minLength: 1, maxLength: 500 }
|
|
text: { type: string, minLength: 1, maxLength: 500 }
|
|
prompt: { type: string, minLength: 1, maxLength: 16000 }
|
|
category: { type: string, minLength: 1, maxLength: 64 }
|
|
priority: { type: integer, minimum: -10000, maximum: 10000 }
|
|
source: { type: string, minLength: 1, maxLength: 64 }
|
|
locale: { type: string, enum: [zh, en] }
|
|
conditions:
|
|
type: array
|
|
maxItems: 20
|
|
items: { type: string, minLength: 1, maxLength: 64 }
|
|
metadata:
|
|
type: object
|
|
additionalProperties: true
|
|
AIHintPack:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [locale, version, cards]
|
|
properties:
|
|
locale: { type: string, enum: [zh, en] }
|
|
generatedAt: { type: string, format: date-time }
|
|
expiresAt: { type: string, format: date-time }
|
|
version: { type: integer, minimum: 1 }
|
|
cards:
|
|
type: array
|
|
maxItems: 500
|
|
items: { $ref: "#/components/schemas/AIHintCard" }
|
|
AdminAIHintPack:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [locale, version, cards]
|
|
properties:
|
|
locale: { type: string, enum: [zh, en] }
|
|
generatedAt: { type: string, format: date-time }
|
|
expiresAt: { type: string, format: date-time }
|
|
intervalHours: { type: integer, minimum: 1, maximum: 168 }
|
|
version: { type: integer, minimum: 0 }
|
|
cards:
|
|
type: array
|
|
maxItems: 500
|
|
items: { $ref: "#/components/schemas/AIHintCard" }
|
|
UpdateAIHintPackRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [cards]
|
|
properties:
|
|
generatedAt: { type: string, format: date-time }
|
|
expiresAt: { type: string, format: date-time }
|
|
intervalHours: { type: integer, minimum: 1, maximum: 168 }
|
|
cards:
|
|
type: array
|
|
maxItems: 500
|
|
items: { $ref: "#/components/schemas/AIHintCard" }
|
|
AIHintManifest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [locales, files]
|
|
properties:
|
|
generatedAt: { type: string, format: date-time }
|
|
expiresAt: { type: string, format: date-time }
|
|
intervalHours: { type: integer, minimum: 1, maximum: 168 }
|
|
locales:
|
|
type: array
|
|
uniqueItems: true
|
|
items: { type: string, enum: [zh, en] }
|
|
files:
|
|
type: object
|
|
additionalProperties:
|
|
type: [string, "null"]
|
|
sources:
|
|
type: object
|
|
additionalProperties:
|
|
type: array
|
|
items: { type: string }
|
|
HintFeedSettings:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- enabled
|
|
- topHubApiKeyConfigured
|
|
- generationIntervalHours
|
|
- holidayCountriesZh
|
|
- holidayCountriesEn
|
|
- weatherCitiesZh
|
|
- weatherCitiesEn
|
|
- googleTrendsGeos
|
|
properties:
|
|
enabled: { type: boolean }
|
|
topHubApiKeyConfigured: { type: boolean }
|
|
generationIntervalHours: { type: integer, minimum: 1, maximum: 168 }
|
|
holidayCountriesZh: { type: string, minLength: 2, maxLength: 255 }
|
|
holidayCountriesEn: { type: string, minLength: 2, maxLength: 255 }
|
|
weatherCitiesZh: { type: string, minLength: 1, maxLength: 2000 }
|
|
weatherCitiesEn: { type: string, minLength: 1, maxLength: 2000 }
|
|
googleTrendsGeos: { type: string, minLength: 2, maxLength: 255 }
|
|
UpdateHintFeedSettingsRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- generationIntervalHours
|
|
- holidayCountriesZh
|
|
- holidayCountriesEn
|
|
- weatherCitiesZh
|
|
- weatherCitiesEn
|
|
- googleTrendsGeos
|
|
properties:
|
|
generationIntervalHours: { type: integer, minimum: 1, maximum: 168 }
|
|
holidayCountriesZh: { type: string, minLength: 2, maxLength: 255 }
|
|
holidayCountriesEn: { type: string, minLength: 2, maxLength: 255 }
|
|
weatherCitiesZh: { type: string, minLength: 1, maxLength: 2000 }
|
|
weatherCitiesEn: { type: string, minLength: 1, maxLength: 2000 }
|
|
googleTrendsGeos: { type: string, minLength: 2, maxLength: 255 }
|
|
HintFeedGenerationStatus:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- enabled
|
|
- outcome
|
|
- intervalHours
|
|
- topHubApiKeyConfigured
|
|
properties:
|
|
enabled: { type: boolean }
|
|
outcome: { type: string, enum: [IDLE, RUNNING, SUCCEEDED, FAILED] }
|
|
intervalHours: { type: integer, minimum: 1, maximum: 168 }
|
|
lastStartedAt: { type: string, format: date-time }
|
|
lastCompletedAt: { type: string, format: date-time }
|
|
lastErrorCode: { type: string, maxLength: 64 }
|
|
nextScheduledAt: { type: string, format: date-time }
|
|
topHubApiKeyConfigured: { type: boolean }
|
|
zhVersion: { type: integer, minimum: 1 }
|
|
zhCardCount: { type: integer, minimum: 0, maximum: 40 }
|
|
enVersion: { type: integer, minimum: 1 }
|
|
enCardCount: { type: integer, minimum: 0, maximum: 40 }
|
|
HintFeedGenerationResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [generationId, generatedAt, zh, en]
|
|
properties:
|
|
generationId: { type: string, format: uuid }
|
|
generatedAt: { type: string, format: date-time }
|
|
zh: { $ref: "#/components/schemas/HintFeedPackGenerationResult" }
|
|
en: { $ref: "#/components/schemas/HintFeedPackGenerationResult" }
|
|
HintFeedPackGenerationResult:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [version, cardCount]
|
|
properties:
|
|
version: { type: integer, minimum: 1 }
|
|
cardCount: { type: integer, minimum: 0, maximum: 40 }
|
|
AdminSessionState:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [authenticated]
|
|
properties:
|
|
authenticated: { type: boolean }
|
|
operatorName: { type: ["string", "null"], minLength: 3, maxLength: 64 }
|
|
role:
|
|
type: ["string", "null"]
|
|
enum: [SUPER_ADMIN, SUPPORT, ANALYST, null]
|
|
description: CSRF material is intentionally not reconstructed or returned by session checks.
|
|
AdminLoginResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [operatorName, role, csrfToken]
|
|
properties:
|
|
operatorName: { type: string, minLength: 3, maxLength: 64 }
|
|
role: { type: string, enum: [SUPER_ADMIN, SUPPORT, ANALYST] }
|
|
csrfToken:
|
|
type: string
|
|
minLength: 32
|
|
maxLength: 512
|
|
description: Returned once for the new session; the CSRF cookie is the reload fallback.
|
|
AdminUsageAggregate:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [kind, requests, chargedCredits, asrMillis, inputTokens, outputTokens]
|
|
properties:
|
|
kind: { type: string }
|
|
requests: { type: integer, format: int64, minimum: 0 }
|
|
chargedCredits: { type: integer, format: int64, minimum: 0 }
|
|
asrMillis: { type: integer, format: int64, minimum: 0 }
|
|
inputTokens: { type: integer, format: int64, minimum: 0 }
|
|
outputTokens: { type: integer, format: int64, minimum: 0 }
|
|
AdminTrendPoint:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [date, registrations, creditsUsed]
|
|
properties:
|
|
date: { type: string, format: date }
|
|
registrations: { type: integer, format: int64, minimum: 0 }
|
|
creditsUsed: { type: integer, format: int64, minimum: 0 }
|
|
AdminStatsPeriod:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [from, until]
|
|
properties:
|
|
from: { type: string, format: date-time, description: Inclusive UTC lower bound. }
|
|
until: { type: string, format: date-time, description: Exclusive current-instant upper bound. }
|
|
AdminOverview:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- period
|
|
- totalUsers
|
|
- activeUsers
|
|
- newUsers
|
|
- totalCreditBalance
|
|
- creditsGranted
|
|
- creditsUsed
|
|
- trend
|
|
- usage
|
|
properties:
|
|
period: { $ref: "#/components/schemas/AdminStatsPeriod" }
|
|
totalUsers: { type: integer, format: int64, minimum: 0 }
|
|
activeUsers:
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
description: Registered accounts with successful AI use or manually committed keyboard input in the period.
|
|
newUsers: { type: integer, format: int64, minimum: 0 }
|
|
totalCreditBalance: { type: integer, format: int64, minimum: 0 }
|
|
creditsGranted: { type: integer, format: int64, minimum: 0 }
|
|
creditsUsed: { type: integer, format: int64, minimum: 0 }
|
|
trend:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminTrendPoint" }
|
|
usage:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminUsageAggregate" }
|
|
AdminFunnelStep:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [label, count]
|
|
properties:
|
|
label:
|
|
type: string
|
|
enum: [成功绑定, 绑定后首次 AI 成功, 完成奖励]
|
|
count: { type: integer, format: int64, minimum: 0 }
|
|
AdminReferralRank:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [userId, invited, qualified, creditsEarned]
|
|
properties:
|
|
userId: { type: string, format: uuid }
|
|
invited: { type: integer, format: int64, minimum: 0 }
|
|
qualified: { type: integer, format: int64, minimum: 0 }
|
|
creditsEarned: { type: integer, format: int64, minimum: 0 }
|
|
AdminReferralOverview:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [period, pendingBindings, ineligibleBindings, funnel, ranking]
|
|
properties:
|
|
period: { $ref: "#/components/schemas/AdminStatsPeriod" }
|
|
pendingBindings: { type: integer, format: int64, minimum: 0 }
|
|
ineligibleBindings: { type: integer, format: int64, minimum: 0 }
|
|
funnel:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminFunnelStep" }
|
|
ranking:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminReferralRank" }
|
|
AdminAnalyticsRate:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [numerator, denominator]
|
|
properties:
|
|
numerator: { type: integer, format: int64, minimum: 0 }
|
|
denominator: { type: integer, format: int64, minimum: 0 }
|
|
percent:
|
|
type: ["number", "null"]
|
|
minimum: 0
|
|
maximum: 100
|
|
description: Null means unavailable, usually because the denominator is zero; clients must not render it as 0%.
|
|
AdminAnalyticsFunnelStep:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [label, count]
|
|
properties:
|
|
label: { type: string, maxLength: 64 }
|
|
count: { type: integer, format: int64, minimum: 0 }
|
|
AdminAnalyticsChannel:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [channel, installations, activated, activationRate]
|
|
properties:
|
|
channel:
|
|
type: string
|
|
enum: [APP_STORE_ORGANIC, REFERRAL, SOCIAL_CONTENT, UNKNOWN]
|
|
installations:
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
description: Installations in this channel with a completed 24-hour observation window.
|
|
activated: { type: integer, format: int64, minimum: 0 }
|
|
activationRate: { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
|
AdminAnalyticsCohort:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [cohortDate, size]
|
|
properties:
|
|
cohortDate: { type: string, format: date }
|
|
size: { type: integer, format: int64, minimum: 0 }
|
|
d1:
|
|
anyOf:
|
|
- { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
|
- { type: "null" }
|
|
d7:
|
|
anyOf:
|
|
- { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
|
- { type: "null" }
|
|
d30:
|
|
anyOf:
|
|
- { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
|
- { type: "null" }
|
|
AdminAnalyticsFeatureUsage:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [feature, executionMode, users, successes]
|
|
properties:
|
|
feature:
|
|
type: string
|
|
enum: [TRANSCRIPTION, POLISH, AI_ASSISTANT, AGENT, HOTWORD, OTHER]
|
|
executionMode: { type: string, enum: [MANAGED, LOCAL, BYOK] }
|
|
users: { type: integer, format: int64, minimum: 0 }
|
|
successes: { type: integer, format: int64, minimum: 0 }
|
|
AdminAnalyticsLatencyBucket:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [bucket, successful, failed]
|
|
properties:
|
|
bucket:
|
|
type: string
|
|
enum: [LT_1S, S1_TO_3, S3_TO_10, S10_TO_30, GTE_30S]
|
|
successful: { type: integer, format: int64, minimum: 0 }
|
|
failed: { type: integer, format: int64, minimum: 0 }
|
|
AdminAnalyticsKeyboardUsage:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- activeUsers
|
|
- activationToInput
|
|
- chineseActiveUsers
|
|
- englishActiveUsers
|
|
- bilingualActiveUsers
|
|
- totalCharacters
|
|
- chineseCharacters
|
|
- englishCharacters
|
|
- otherCharacters
|
|
- inputSessions
|
|
- chineseOnlySessions
|
|
- englishOnlySessions
|
|
- mixedLanguageSessions
|
|
- otherOnlySessions
|
|
properties:
|
|
activeUsers: { type: integer, format: int64, minimum: 0 }
|
|
activationToInput: { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
|
chineseActiveUsers: { type: integer, format: int64, minimum: 0 }
|
|
englishActiveUsers: { type: integer, format: int64, minimum: 0 }
|
|
bilingualActiveUsers: { type: integer, format: int64, minimum: 0 }
|
|
totalCharacters: { type: integer, format: int64, minimum: 0 }
|
|
chineseCharacters: { type: integer, format: int64, minimum: 0 }
|
|
englishCharacters: { type: integer, format: int64, minimum: 0 }
|
|
otherCharacters: { type: integer, format: int64, minimum: 0 }
|
|
chineseSharePercent: { type: ["number", "null"], minimum: 0, maximum: 100 }
|
|
englishSharePercent: { type: ["number", "null"], minimum: 0, maximum: 100 }
|
|
inputSessions: { type: integer, format: int64, minimum: 0 }
|
|
averageCharactersPerInputSession: { type: ["number", "null"], minimum: 0 }
|
|
chineseOnlySessions: { type: integer, format: int64, minimum: 0 }
|
|
englishOnlySessions: { type: integer, format: int64, minimum: 0 }
|
|
mixedLanguageSessions: { type: integer, format: int64, minimum: 0 }
|
|
otherOnlySessions: { type: integer, format: int64, minimum: 0 }
|
|
AdminProductAnalytics:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- period
|
|
- northStar
|
|
- growth
|
|
- activity
|
|
- consumption
|
|
- monetization
|
|
- growthFunnel
|
|
- retention
|
|
- aiFeatures
|
|
- keyboardUsage
|
|
- referralSignals
|
|
- referralFunnel
|
|
- guardrails
|
|
properties:
|
|
period: { $ref: "#/components/schemas/AdminStatsPeriod" }
|
|
northStar:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [weeklyAiActiveUsers, previousWeeklyAiActiveUsers]
|
|
properties:
|
|
weeklyAiActiveUsers: { type: integer, format: int64, minimum: 0 }
|
|
previousWeeklyAiActiveUsers: { type: integer, format: int64, minimum: 0 }
|
|
weekOverWeekPercent: { type: ["number", "null"] }
|
|
growth:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [newInstallations, newAccounts, activation24h, channels]
|
|
properties:
|
|
newInstallations: { type: integer, format: int64, minimum: 0 }
|
|
newAccounts: { type: integer, format: int64, minimum: 0 }
|
|
activation24h: { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
|
medianTimeToValueMinutes: { type: ["number", "null"], minimum: 0 }
|
|
channels:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminAnalyticsChannel" }
|
|
activity:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [dau, wau, mau, successfulAiRequests]
|
|
properties:
|
|
dau:
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
description: Distinct AI value-active identities in the rolling 1-day window ending at period.until.
|
|
wau:
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
description: Distinct AI value-active identities in the rolling 7-day window ending at period.until.
|
|
mau:
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
description: Distinct AI value-active identities in the rolling 30-day window ending at period.until.
|
|
stickinessPercent: { type: ["number", "null"], minimum: 0, maximum: 100 }
|
|
successfulAiRequests: { type: integer, format: int64, minimum: 0 }
|
|
successfulRequestsPerActiveUser: { type: ["number", "null"], minimum: 0 }
|
|
consumption:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [totalCredits]
|
|
properties:
|
|
totalCredits: { type: integer, format: int64, minimum: 0 }
|
|
averageDailyCreditsPerActiveUser: { type: ["number", "null"], minimum: 0 }
|
|
medianUserDailyCredits: { type: ["number", "null"], minimum: 0 }
|
|
averageCreditsPerManagedRequest: { type: ["number", "null"], minimum: 0 }
|
|
monetization:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
[payingUsers, purchases, creditsPurchased, conversion7d, conversion30d, repeatPurchaseRate, purchaseFunnel, cancelledUsers]
|
|
properties:
|
|
payingUsers: { type: integer, format: int64, minimum: 0 }
|
|
purchases: { type: integer, format: int64, minimum: 0 }
|
|
creditsPurchased: { type: integer, format: int64, minimum: 0 }
|
|
conversion7d:
|
|
$ref: "#/components/schemas/AdminAnalyticsRate"
|
|
description: Conversion for account cohorts whose full 7-day observation window matures inside the selected report period.
|
|
conversion30d:
|
|
$ref: "#/components/schemas/AdminAnalyticsRate"
|
|
description: Conversion for account cohorts whose full 30-day observation window matures inside the selected report period.
|
|
repeatPurchaseRate: { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
|
purchaseFunnel:
|
|
type: array
|
|
description: Strict installation cohort from purchase view through server-verified StoreKit purchase.
|
|
items: { $ref: "#/components/schemas/AdminAnalyticsFunnelStep" }
|
|
cancelledUsers: { type: integer, format: int64, minimum: 0 }
|
|
growthFunnel:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminAnalyticsFunnelStep" }
|
|
retention:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminAnalyticsCohort" }
|
|
aiFeatures:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminAnalyticsFeatureUsage" }
|
|
keyboardUsage:
|
|
$ref: "#/components/schemas/AdminAnalyticsKeyboardUsage"
|
|
referralSignals:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [shared, opened]
|
|
description: Directional signals only; these counts are not funnel stages.
|
|
properties:
|
|
shared: { type: integer, format: int64, minimum: 0 }
|
|
opened: { type: integer, format: int64, minimum: 0 }
|
|
referralFunnel:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminAnalyticsFunnelStep" }
|
|
guardrails:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [clientAiSuccessRate, managedSuccessRate, creditBlockedUsers, latencyBuckets]
|
|
properties:
|
|
clientAiSuccessRate: { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
|
managedSuccessRate: { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
|
creditBlockedUsers: { type: integer, format: int64, minimum: 0 }
|
|
latencyBuckets:
|
|
type: array
|
|
description: Client AI terminal events grouped into declared duration buckets.
|
|
items: { $ref: "#/components/schemas/AdminAnalyticsLatencyBucket" }
|
|
AdminUserSummary:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [userId, displayName, status, creditBalance, consumedCredits, createdAt]
|
|
properties:
|
|
userId: { type: string, format: uuid }
|
|
displayName: { type: string }
|
|
status: { type: string, enum: [active, suspended, closed] }
|
|
creditBalance: { type: integer, format: int64, minimum: 0 }
|
|
consumedCredits: { type: integer, format: int64, minimum: 0 }
|
|
createdAt: { type: string, format: date-time }
|
|
AdminUserPage:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [items]
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminUserSummary" }
|
|
nextCursor: { type: ["string", "null"] }
|
|
AdminUserReferral:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [invitedUsers, rewardedInvites]
|
|
properties:
|
|
inviterUserId: { type: ["string", "null"], format: uuid }
|
|
invitedUsers: { type: integer, format: int64, minimum: 0 }
|
|
rewardedInvites: { type: integer, format: int64, minimum: 0 }
|
|
AdminUserDetail:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- userId
|
|
- displayName
|
|
- status
|
|
- creditBalance
|
|
- consumedCredits
|
|
- createdAt
|
|
- qualifiedUsage
|
|
- usage
|
|
- referral
|
|
properties:
|
|
userId: { type: string, format: uuid }
|
|
displayName: { type: string }
|
|
status: { type: string, enum: [active, suspended, closed] }
|
|
creditBalance: { type: integer, format: int64, minimum: 0 }
|
|
consumedCredits: { type: integer, format: int64, minimum: 0 }
|
|
createdAt: { type: string, format: date-time }
|
|
lastActiveAt: { type: ["string", "null"], format: date-time }
|
|
qualifiedUsage: { type: boolean }
|
|
referralCode: { type: ["string", "null"] }
|
|
referredByUserId: { type: ["string", "null"], format: uuid }
|
|
usage:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminUsageAggregate" }
|
|
referral: { $ref: "#/components/schemas/AdminUserReferral" }
|
|
LedgerEntryType:
|
|
type: string
|
|
enum:
|
|
- SIGNUP_TRIAL
|
|
- MANUAL_GRANT
|
|
- USAGE_RESERVE
|
|
- USAGE_SETTLE
|
|
- USAGE_RELEASE
|
|
- USAGE_REFUND
|
|
- REFERRAL_INVITER
|
|
- REFERRAL_INVITEE
|
|
- STOREKIT_PURCHASE
|
|
- SUBSCRIPTION_GRANT
|
|
AdminLedgerDetails:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [kind]
|
|
description: Privacy-minimized source metadata. Missing source associations omit the details object.
|
|
properties:
|
|
kind: { type: string, enum: [manualGrant, storeKit, referral, usage] }
|
|
reason: { type: string, description: Present for manualGrant details }
|
|
operatorName: { type: string, description: Present for manualGrant details }
|
|
productId: { type: string, description: Present for storeKit details }
|
|
transactionId: { type: string, description: Present for storeKit details }
|
|
originalTransactionId: { type: string, description: Present for storeKit details }
|
|
environment: { type: string, enum: [SANDBOX, PRODUCTION], description: Present for storeKit details }
|
|
purchasedAt: { type: string, format: date-time, description: Present for storeKit details }
|
|
role: { type: string, enum: [inviter, invitee], description: Present for referral details }
|
|
relatedUserId: { type: string, format: uuid, description: Present for referral details }
|
|
reservationId: { type: string, format: uuid, description: Present for usage details }
|
|
AdminLedgerEntry:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [entryId, userId, type, entryType, amount, balanceAfter, reasonCode, createdAt]
|
|
properties:
|
|
entryId: { type: string, format: uuid }
|
|
userId: { type: string, format: uuid }
|
|
type: { type: string, enum: [grant, reserve, settle, refund] }
|
|
entryType: { $ref: "#/components/schemas/LedgerEntryType" }
|
|
amount: { type: integer, format: int64 }
|
|
balanceAfter: { type: integer, format: int64, minimum: 0 }
|
|
reasonCode:
|
|
allOf:
|
|
- $ref: "#/components/schemas/LedgerEntryType"
|
|
deprecated: true
|
|
description: Compatibility alias for entryType
|
|
referenceId: { type: ["string", "null"], format: uuid }
|
|
usageType:
|
|
type: ["string", "null"]
|
|
enum: [polish, asr, ai, agent, hotword, null]
|
|
description: Product usage associated with this ledger operation
|
|
details: { $ref: "#/components/schemas/AdminLedgerDetails" }
|
|
createdAt: { type: string, format: date-time }
|
|
AdminLedgerPage:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [items]
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminLedgerEntry" }
|
|
nextCursor: { type: ["string", "null"] }
|
|
AdminGrantResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [transactionId, balanceAfter]
|
|
properties:
|
|
transactionId: { type: string, format: uuid }
|
|
balanceAfter: { type: integer, format: int64, minimum: 0 }
|
|
AdminAudit:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [auditId, operatorName, action, targetType, targetId, result, createdAt]
|
|
properties:
|
|
auditId: { type: string, format: uuid }
|
|
operatorName: { type: string }
|
|
action: { type: string }
|
|
targetType: { type: string }
|
|
targetId: { type: string }
|
|
requestId: { type: ["string", "null"] }
|
|
result: { type: string, enum: [success, rejected] }
|
|
createdAt: { type: string, format: date-time }
|
|
AdminAuditPage:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [items]
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminAudit" }
|
|
nextCursor: { type: ["string", "null"] }
|
|
AdminOperator:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- operatorId
|
|
- username
|
|
- role
|
|
- enabled
|
|
- failedLoginCount
|
|
- createdAt
|
|
- updatedAt
|
|
properties:
|
|
operatorId: { type: string, format: uuid }
|
|
username:
|
|
type: string
|
|
pattern: "^[a-z0-9][a-z0-9._@-]{2,63}$"
|
|
role: { type: string, enum: [SUPER_ADMIN, SUPPORT, ANALYST] }
|
|
enabled: { type: boolean }
|
|
failedLoginCount: { type: integer, minimum: 0 }
|
|
lockedUntil: { type: ["string", "null"], format: date-time }
|
|
lastLoginAt: { type: ["string", "null"], format: date-time }
|
|
createdAt: { type: string, format: date-time }
|
|
updatedAt: { type: string, format: date-time }
|
|
AdminOperatorPage:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [items]
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/AdminOperator" }
|
|
nextCursor: { type: ["string", "null"] }
|
|
AdminOperatorCreateRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [username, password, role]
|
|
properties:
|
|
username:
|
|
type: string
|
|
minLength: 3
|
|
maxLength: 64
|
|
pattern: "^[A-Za-z0-9][A-Za-z0-9._@-]{2,63}$"
|
|
password: { type: string, minLength: 12, maxLength: 1024 }
|
|
role: { type: string, enum: [SUPER_ADMIN, SUPPORT, ANALYST] }
|
|
AdminOperatorPasswordRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [password]
|
|
properties:
|
|
password: { type: string, minLength: 12, maxLength: 1024 }
|
|
AdminOperatorProvisioning:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [operatorId, totpSecret, otpauthUri]
|
|
properties:
|
|
operatorId: { type: string, format: uuid }
|
|
operator:
|
|
oneOf:
|
|
- $ref: "#/components/schemas/AdminOperator"
|
|
- type: "null"
|
|
totpSecret:
|
|
type: string
|
|
pattern: "^[A-Z2-7]{32}$"
|
|
description: 160-bit Base32 secret returned once; never persisted in plaintext.
|
|
otpauthUri:
|
|
type: string
|
|
pattern: "^otpauth://totp/"
|
|
description: Provisioning URI returned once; never persisted.
|
|
AppleSignInRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [identityToken, authorizationCode, nonce]
|
|
properties:
|
|
identityToken:
|
|
type: string
|
|
description: Identity token returned by Sign in with Apple.
|
|
authorizationCode:
|
|
type: string
|
|
description: Single-use authorization code returned by Sign in with Apple.
|
|
nonce:
|
|
type: string
|
|
description: Raw nonce whose lowercase SHA-256 hex digest was sent to Apple.
|
|
displayName:
|
|
type: ["string", "null"]
|
|
maxLength: 128
|
|
description: Optional first-authorization Apple name used only to seed the nickname.
|
|
deviceCheckToken:
|
|
type: ["string", "null"]
|
|
description: Ephemeral DeviceCheck token; never persisted in plaintext.
|
|
appAttest:
|
|
oneOf:
|
|
- $ref: "#/components/schemas/AppAttestAssertion"
|
|
- type: "null"
|
|
AppAttestAssertion:
|
|
type: object
|
|
description: |
|
|
For Apple sign-in, generate the assertion with `clientDataHash` equal to
|
|
SHA-256 of the exact UTF-8 payload below, including the final line feed:
|
|
|
|
```
|
|
osg-app-attest-v1
|
|
purpose=apple-sign-in
|
|
challenge=<challenge>
|
|
identity_token_sha256=<identity-token-digest>
|
|
authorization_code_sha256=<authorization-code-digest>
|
|
nonce_sha256=<raw-nonce-digest>
|
|
```
|
|
|
|
`challenge` is the Base64URL value returned by `/v1/integrity/challenges`.
|
|
Each digest is SHA-256 of the corresponding UTF-8 request value, encoded
|
|
as unpadded Base64URL. The server reconstructs this payload and never
|
|
trusts a client-supplied hash.
|
|
additionalProperties: false
|
|
required: [keyId, challengeId, challenge, assertion]
|
|
properties:
|
|
keyId: { type: string, maxLength: 128 }
|
|
challengeId: { type: string, format: uuid }
|
|
challenge: { type: string, description: Base64URL challenge returned by the server }
|
|
assertion: { type: string, contentEncoding: base64 }
|
|
SessionTokenResponse:
|
|
type: object
|
|
required: [accountId, tokenType, accessToken, accessTokenExpiresAtEpochSeconds, refreshToken, refreshTokenExpiresAtEpochSeconds]
|
|
properties:
|
|
accountId: { type: string, format: uuid }
|
|
tokenType: { type: string, const: Bearer }
|
|
accessToken: { type: string }
|
|
accessTokenExpiresAtEpochSeconds: { type: integer, format: int64 }
|
|
refreshToken: { type: string }
|
|
refreshTokenExpiresAtEpochSeconds: { type: integer, format: int64 }
|
|
SessionTokenEnvelope:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [data]
|
|
properties:
|
|
data: { $ref: "#/components/schemas/SessionTokenResponse" }
|
|
Account:
|
|
type: object
|
|
required: [id, createdAtEpochSeconds]
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
createdAtEpochSeconds: { type: integer, format: int64 }
|
|
displayName:
|
|
type: ["string", "null"]
|
|
maxLength: 64
|
|
AccountEnvelope:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [data]
|
|
properties:
|
|
data: { $ref: "#/components/schemas/Account" }
|
|
UpdateAccountProfileRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [displayName]
|
|
properties:
|
|
displayName:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 64
|
|
DeleteAccountRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [identityToken, authorizationCode, nonce]
|
|
properties:
|
|
identityToken: { type: string, maxLength: 16384 }
|
|
authorizationCode: { type: string, maxLength: 4096 }
|
|
nonce: { type: string, maxLength: 512 }
|
|
CreditAccount:
|
|
type: object
|
|
additionalProperties: true
|
|
required: [userId, balance, lifetimeUsed]
|
|
properties:
|
|
userId: { type: string, format: uuid }
|
|
balance: { type: integer, format: int64, minimum: 0 }
|
|
lifetimeUsed:
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
description: Settled usage minus credits returned by refunds
|
|
LedgerEntry:
|
|
type: object
|
|
additionalProperties: true
|
|
required: [id, amountDelta, balanceAfter]
|
|
properties:
|
|
id: { type: string, format: uuid }
|
|
amountDelta: { type: integer, format: int64 }
|
|
balanceAfter: { type: integer, format: int64, minimum: 0 }
|
|
StoreKitProduct:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [productId, credits]
|
|
properties:
|
|
productId:
|
|
type: string
|
|
enum: [500tks, 1500tks, 3000tks]
|
|
credits: { type: integer, format: int64, minimum: 1 }
|
|
StoreKitTransactionRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [signedTransaction]
|
|
properties:
|
|
signedTransaction:
|
|
type: string
|
|
minLength: 100
|
|
maxLength: 32768
|
|
description: StoreKit 2 VerificationResult.jwsRepresentation
|
|
StoreKitPurchase:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [transactionId, productId, creditsGranted, balanceAfter, replayed]
|
|
properties:
|
|
transactionId: { type: string, pattern: "^[0-9]{1,64}$" }
|
|
productId: { type: string, minLength: 3, maxLength: 128 }
|
|
creditsGranted: { type: integer, format: int64, minimum: 1 }
|
|
balanceAfter: { type: integer, format: int64, minimum: 0 }
|
|
replayed: { type: boolean }
|
|
StoreKitTransactionHistoryItem:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
[transactionId, productId, creditsGranted, balanceAfter, purchasedAt, status]
|
|
properties:
|
|
transactionId: { type: string, pattern: "^[0-9]{1,64}$" }
|
|
productId: { type: string, minLength: 3, maxLength: 128 }
|
|
creditsGranted: { type: integer, format: int64, minimum: 1 }
|
|
balanceAfter: { type: integer, format: int64, minimum: 0 }
|
|
purchasedAt: { type: string, format: date-time }
|
|
status: { type: string, enum: [credited] }
|
|
StoreKitTransactionHistory:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [items, nextCursor]
|
|
properties:
|
|
items:
|
|
type: array
|
|
items: { $ref: "#/components/schemas/StoreKitTransactionHistoryItem" }
|
|
nextCursor:
|
|
type: ["string", "null"]
|
|
maxLength: 256
|
|
AppleAppSiteAssociation:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [applinks]
|
|
properties:
|
|
applinks:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [details]
|
|
properties:
|
|
details:
|
|
type: array
|
|
items:
|
|
type: object
|
|
required: [appIDs, components]
|
|
properties:
|
|
appIDs:
|
|
type: array
|
|
items: { type: string }
|
|
components:
|
|
type: array
|
|
items: { type: object, additionalProperties: true }
|
|
ReferralCode:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [code, inviteUrl, createdAt]
|
|
properties:
|
|
code:
|
|
type: string
|
|
pattern: "^[A-Za-z0-9_-]{22}$"
|
|
description: Permanent opaque identifier assigned once to the account.
|
|
inviteUrl:
|
|
type: string
|
|
format: uri
|
|
description: Stable first-party invitation URL containing the permanent code.
|
|
campaignId:
|
|
type: ["string", "null"]
|
|
format: uuid
|
|
description: Legacy creation metadata; it does not limit the invitation lifetime.
|
|
createdAt: { type: string, format: date-time }
|
|
ReferralProfile:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [code]
|
|
properties:
|
|
code: { $ref: "#/components/schemas/ReferralCode" }
|
|
binding:
|
|
type: ["object", "null"]
|
|
additionalProperties: true
|
|
RedeemReferralRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [code]
|
|
properties:
|
|
code: { type: string, pattern: "^[A-Za-z0-9_-]{22}$" }
|
|
AppAttestChallengeRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [purpose, keyId]
|
|
properties:
|
|
purpose: { type: string, enum: [attestation, assertion] }
|
|
keyId: { type: string, maxLength: 128 }
|
|
AppAttestChallengeResponse:
|
|
type: object
|
|
required: [challengeId, challenge, expiresAtEpochSeconds]
|
|
properties:
|
|
challengeId: { type: string, format: uuid }
|
|
challenge: { type: string }
|
|
expiresAtEpochSeconds: { type: integer, format: int64 }
|
|
AppAttestationRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [challengeId, challenge, keyId, attestationObject]
|
|
properties:
|
|
challengeId: { type: string, format: uuid }
|
|
challenge: { type: string }
|
|
keyId: { type: string }
|
|
attestationObject: { type: string, contentEncoding: base64 }
|
|
AppAssertionRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [challengeId, challenge, keyId, assertion, clientDataHash]
|
|
properties:
|
|
challengeId: { type: string, format: uuid }
|
|
challenge: { type: string }
|
|
keyId: { type: string }
|
|
assertion: { type: string, contentEncoding: base64 }
|
|
clientDataHash: { type: string, description: Base64URL SHA-256 of the echoed challenge }
|
|
TextGatewayRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [input]
|
|
properties:
|
|
input: { type: string, minLength: 1, maxLength: 32000 }
|
|
context: { type: ["string", "null"], maxLength: 32000 }
|
|
maxOutputTokens:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 4096
|
|
default: 512
|
|
description: |
|
|
Requested output budget. The server clamps dictation polish and
|
|
edit-last-input to 512 tokens; translation, clipboard transform,
|
|
and custom skill to 2,048; and reasoning tasks to 4,096.
|
|
temperature: { type: number, minimum: 0, maximum: 1, default: 0.2 }
|
|
stream: { type: boolean, default: false }
|
|
taskKind:
|
|
type: ["string", "null"]
|
|
enum:
|
|
- dictation_polish
|
|
- translation
|
|
- edit_last_input
|
|
- ai_question
|
|
- clipboard_transform
|
|
- custom_skill
|
|
- agent_planning
|
|
- null
|
|
description: |
|
|
Optional deterministic task selector. Allowed combinations are:
|
|
`polish` with `dictation_polish`, `translation`, or `edit_last_input`;
|
|
`ai` with `ai_question`, `clipboard_transform`, or `custom_skill`;
|
|
and `agent` with `agent_planning`. Omission defaults respectively to
|
|
`dictation_polish`, `ai_question`, and `agent_planning`. A mismatch
|
|
returns `400 invalid_request`.
|
|
requestSource:
|
|
type: ["string", "null"]
|
|
enum: [hotword, null]
|
|
description: Optional product entry point; hotword is accepted only for AI requests
|
|
requestPurpose:
|
|
type: ["string", "null"]
|
|
enum: [oobe, null]
|
|
description: |
|
|
Optional server-audited billing purpose. Account grants accept `oobe`
|
|
only for complimentary dictation polish. Anonymous OOBE grants require
|
|
`oobe` together with an `oobeFeature`.
|
|
oobeFeature:
|
|
type: ["string", "null"]
|
|
enum: [voice_input, clipboard_translate, clipboard_reply, ask_ai, null]
|
|
description: |
|
|
Required for anonymous OOBE grants. The server validates that the
|
|
feature matches the requested capability and task kind, and allows
|
|
each feature to succeed only once per installation-bound subject.
|
|
CreateOobeGrantRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [challengeId, challenge, keyId, installationId, assertion]
|
|
properties:
|
|
challengeId: { type: string, format: uuid }
|
|
challenge: { type: string, description: Base64URL challenge returned by the integrity API }
|
|
keyId: { type: string, minLength: 1, maxLength: 256 }
|
|
installationId: { type: string, format: uuid }
|
|
assertion: { type: string, contentEncoding: base64 }
|
|
RefreshOobeGrantRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [refreshToken]
|
|
properties:
|
|
refreshToken: { type: string, minLength: 32, maxLength: 512 }
|
|
OobeGrantTokens:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
[grantId, scopes, features, accessToken, accessExpiresAt, refreshToken, refreshExpiresAt]
|
|
properties:
|
|
grantId: { type: string, format: uuid }
|
|
scopes:
|
|
type: array
|
|
uniqueItems: true
|
|
items: { type: string, enum: [polish, ai] }
|
|
features:
|
|
type: array
|
|
uniqueItems: true
|
|
items: { type: string, enum: [voice_input, clipboard_translate, clipboard_reply, ask_ai] }
|
|
accessToken: { type: string }
|
|
accessExpiresAt: { type: string, format: date-time }
|
|
refreshToken: { type: string }
|
|
refreshExpiresAt: { type: string, format: date-time }
|
|
CreateGatewayGrantRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [scopes]
|
|
properties:
|
|
scopes:
|
|
type: array
|
|
uniqueItems: true
|
|
minItems: 1
|
|
items: { type: string, enum: [polish, ai, agent, asr] }
|
|
lifetimeSeconds: { type: ["integer", "null"], format: int64, minimum: 1 }
|
|
RefreshGatewayGrantRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [refreshToken]
|
|
properties:
|
|
refreshToken: { type: string, minLength: 32 }
|
|
GatewayGrantTokens:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [grantId, scopes, accessToken, accessExpiresAt, refreshToken, refreshExpiresAt]
|
|
properties:
|
|
grantId: { type: string, format: uuid }
|
|
scopes:
|
|
type: array
|
|
uniqueItems: true
|
|
items: { type: string, enum: [polish, ai, agent, asr] }
|
|
accessToken: { type: string }
|
|
accessExpiresAt: { type: string, format: date-time }
|
|
refreshToken: { type: string }
|
|
refreshExpiresAt: { type: string, format: date-time }
|
|
CreateAsrSessionRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [estimatedDurationMillis]
|
|
properties:
|
|
format: { type: string, enum: [pcm, wav, ogg, mp3], default: pcm }
|
|
codec: { type: string, enum: [raw, opus], default: raw }
|
|
sampleRate: { type: integer, const: 16000 }
|
|
bits: { type: integer, const: 16 }
|
|
channels: { type: integer, minimum: 1, maximum: 2, default: 1 }
|
|
language: { type: ["string", "null"], maxLength: 32 }
|
|
estimatedDurationMillis: { type: integer, minimum: 1, maximum: 600000 }
|
|
CreateAsrSessionResponse:
|
|
type: object
|
|
required: [sessionId, websocketPath, maxFrameBytes, idleTimeoutMillis]
|
|
properties:
|
|
sessionId: { type: string, format: uuid }
|
|
websocketPath: { type: string }
|
|
maxFrameBytes: { type: integer }
|
|
idleTimeoutMillis: { type: integer, format: int64 }
|
|
ApiError:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [code, message]
|
|
properties:
|
|
code: { type: string }
|
|
message: { type: string }
|
|
ApiErrorEnvelope:
|
|
type: object
|
|
additionalProperties: false
|
|
required: [error]
|
|
properties:
|
|
error: { $ref: "#/components/schemas/ApiError" }
|
|
GatewayError:
|
|
type: object
|
|
required: [code, message, requestId]
|
|
properties:
|
|
code: { type: string }
|
|
message: { type: string }
|
|
requestId: { type: string }
|