VoidEx API (1.0.0)

Download OpenAPI specification:

License: Proprietary

VoidEx is a programmable, galaxy-wide synthetic stock exchange simulator. This API allows you to list stocks, place trades, check your wallet, and view fictional market news.

Response envelope

Every handled route responds with exactly one of two JSON bodies, defined once in this spec and referenced everywhere:

  • success → ApiResponse (statusCode, meta, links, relationships, data)
  • error → ErrorResponse (statusCode, meta, errors[] — each entry has a required message, optional detail, and optional source.pointer / source.parameter locating the offending body field or request parameter)

The one exception is POST /auth/logout, which answers 204 with an empty body. Paths outside the route table answer an enveloped 404 {errors: [{message: "Not found"}]} from the app-level catch-all — never a bare Express 404.

Rate limiting

Every route except GET /health is rate-limited per role and per route id (unauthenticated 5/60s · authenticated 50/60s · verified 100/60s · admin unlimited, limiter bypassed). Every rate-limited response — success and error — carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (window end, Unix epoch seconds), each duplicated in a legacy X-RateLimit-* twin with an identical value. A 429 adds Retry-After (whole seconds) and meta.retryAfter. Admin-role callers bypass the limiter and receive no rate-limit headers.

Auth tiers

  • public — no token: GET /health, POST /auth/register, POST /auth/login, POST /auth/password-reset, GET /calendar/now, GET /calendar/convert, GET /ledgr/ping, GET /public/stocks, GET /public/news, GET /public/calendar/events, GET /public/leaderboard, GET /public/ticker, GET /public/movers, GET /public/merch

  • bearerAuthorization: Bearer <ID token>; missing/invalid token → enveloped 401 {message: "Unauthorized"}

  • admin — bearer + the role: "admin" custom claim; a non-admin gets 403 {message: "Forbidden"} before rate limiting

Versioning

Additive-only within /v1: new routes, new optional parameters, and new response fields may appear; shipped fields, paths, status codes, and auth levels never change. Breaking changes ship as a new version mount.

Health

Service health. The only route with no rate limiter.

Service health check

Public; the only route with no rate limiter. operational and degraded answer 200; impaired answers 503 — with the success envelope in all three cases.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Public

Unauthenticated read-only surface for the deployed status dashboard.

List stocks (public dashboard)

Public read-only mirror of GET /market for the deployed status dashboard. No authentication; unauthenticated rate limit applies. Same additive q/sector/faction/sort/order screening params as GET /market.

query Parameters
limit
integer [ 1 .. 500 ]
Default: 100
offset
integer >= 0
Default: 0
q
string

Case-insensitive substring match over symbol and company name.

sector
string

Exact-match sector filter (the trading "industry").

faction
string

Exact-match issuer-faction filter.

sort
string
Enum: "price" "change" "symbol"

Sort key; change sorts on priceChangePercent.

order
string
Default: "asc"
Enum: "asc" "desc"

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

List news (public dashboard)

Public read-only mirror of GET /news for the deployed status dashboard.

query Parameters
limit
integer [ 1 .. 500 ]
Default: 10
offset
integer >= 0
Default: 0
company
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

List active + upcoming calendar events (public dashboard)

Public read-only mirror of GET /calendar/events for the deployed status dashboard. Returns every event that has not yet expired — currently in-effect events plus scheduled future ones.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Net-worth leaderboard (public dashboard)

Read-only rankings for the public dashboard. Reads leaderboard/top; returns an empty entries array when not yet computed. Omits player uid from each entry.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Live market ticker-tape feed (public)

The sim-maintained live ticker-tape strip. Unauthenticated; served from the bounded market/ticker maintained doc (one set-merge write per tick — never an unbounded append). Carries a compact symbol strip plus top movers/losers by percent change. Returns an empty strip (entries: [], computedAt: null) before the sim first computes one. Supersedes the reserved-but-never-built GET /market/ticker/feed census entry.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Periodic market-movers digest (public)

The sim-maintained 'market movers' digest. Unauthenticated; served from the bounded market/moversDigest maintained doc (one set-merge write per digest period — never an unbounded append). The sim broadcasts the same digest as a global SYSTEM message on a fixed per-period id and refreshes this read doc. Carries the digest subject/body plus the bounded top gainers/losers by percent — redacted market data only (no PII). Returns an empty digest (subject: null, computedAt: null) before the sim first broadcasts one.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Market-day spectator replay (public)

The market-day spectator replay. Unauthenticated; serves the sim-maintained market/spectatorReplay doc's BOUNDED, DOWNSAMPLED frames — at most one frame per period bucket (never raw per-minute for every stock), a drop-oldest ring capped at the replay maximum so the doc can never grow unbounded. Each frame is the ticker's highlight reel (topMovers/topLosers) at a moment in the day — redacted market data only (no PII). PAGINATED (limit/offset; meta carries { total, limit, offset }), ordered oldest-first (chronological replay). Returns an empty page before the first frame.

query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Market-day recap (public)

The market-day recap. Unauthenticated; returns the day's TOP MOVERS/LOSERS (from the latest spectator-replay frame — reuses the movers reel) plus the day's KEY EVENTS/NEWS (a BOUNDED read of the most-recent headlines). Redacted market data only — no PII. Returns an empty recap (computedAt: null, empty lists) before the sim first records a frame.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Auth

Registration, login, token refresh, profile, logout.

Register a new user

Creates the user account and answers 201 {uid}. It does not issue tokens — clients log in afterward.

Request Body schema: application/json
required
email
required
string
username
required
string
password
required
string <password>

Responses

Request samples

Content type
application/json
{
  • "email": "voidtrader123@domain.com",
  • "username": "voidtrader123",
  • "password": "superSecurePassword!"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Login and receive an ID token

Request Body schema: application/json
required
email
required
string
password
required
string <password>

Responses

Request samples

Content type
application/json
{
  • "email": "voidtrader123@domain.com",
  • "password": "superSecurePassword!"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Start SMS MFA sign-in (send verification code)

Request Body schema: application/json
required
mfaPendingCredential
required
string
mfaEnrollmentId
required
string

Responses

Request samples

Content type
application/json
{
  • "mfaPendingCredential": "string",
  • "mfaEnrollmentId": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Complete MFA sign-in with verification code

Request Body schema: application/json
required
mfaPendingCredential
required
string
mfaEnrollmentId
required
string
verificationCode
required
string
factorType
required
string
Enum: "phone" "totp"
sessionInfo
string

Responses

Request samples

Content type
application/json
{
  • "mfaPendingCredential": "string",
  • "mfaEnrollmentId": "string",
  • "verificationCode": "string",
  • "factorType": "phone",
  • "sessionInfo": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

List enrolled MFA factors for the authenticated user

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Begin TOTP MFA enrollment

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Complete TOTP MFA enrollment

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Begin SMS MFA enrollment

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Complete SMS MFA enrollment

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Remove an enrolled MFA factor

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Refresh an access token using a valid refresh token

Requires a still-valid bearer token and the refresh token in the body — an expired ID token must re-login.

Authorizations:
bearerAuth
Request Body schema: application/json
required
refreshToken
required
string

Responses

Request samples

Content type
application/json
{
  • "refreshToken": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get current authenticated user

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Change password for the signed-in user

Verifies the current password, sets a new one, and returns refreshed tokens (ADR 0032). API-native clients install the new idToken in memory.

Authorizations:
bearerAuth
Request Body schema: application/json
required
currentPassword
required
string
newPassword
required
string >= 6 characters

Responses

Request samples

Content type
application/json
{
  • "currentPassword": "string",
  • "newPassword": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update the signed-in user's profile

Owner-scoped profile mutation (ADR 0032). v1 supports username only — updates Firestore and Firebase Auth displayName.

Authorizations:
bearerAuth
Request Body schema: application/json
required
username
required
string [ 1 .. 64 ] characters

Responses

Request samples

Content type
application/json
{
  • "username": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Log out the current session

Revokes the caller's refresh tokens. Success is 204 with an empty body — the one non-enveloped success on the surface.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Send account verification email again

Sends a branded verification email to the account's address. Requires a bearer token for an account whose email is not yet verified.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Request a password reset email

Sends a branded password-reset email when an account exists for the supplied address. Always returns success for valid email format to avoid account enumeration.

Request Body schema: application/json
required
email
required
string

Responses

Request samples

Content type
application/json
{
  • "email": "voidtrader123@domain.com"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Calendar

Galactic Standard Calendar time and events. Event dates cross the wire as ISO-8601 strings.

Get current Galactic Calendar time

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Convert between UTC and Galactic Calendar time

query Parameters
direction
required
string
Enum: "utc" "gsc"

Conversion direction:

  • gsc converts ISO UTC to Galactic Time
  • utc converts Galactic Time to ISO UTC
value
required
string

The input time value to convert:

  • If direction=gsc, this must be an ISO8601 date-time string (e.g. 2025-07-09T18:10:15.019Z)
  • If direction=utc, this must be a Galactic Standard time string (e.g. ☉7461 △06 □24 21:55:0095.019 or 7461.06.24.21:55:0095.019)

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get active + upcoming calendar events

Every event that has not yet expired (expires in the future, or no expiry) — currently in-effect events plus scheduled future ones — ordered by start. Dates are serialized to ISO-8601 strings on the way out.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get a specific Galactic Calendar event

Authorizations:
bearerAuth
path Parameters
id
required
string

Event document ID

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

LEDGR

The LEDGR AI ledger-keeper — status, logs, echo.

Ping LEDGR for status and latency check

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get current LEDGR AI status and metadata

Serves the ledgr/status singleton; a missing doc degrades to hard-coded defaults.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Retrieve LEDGR system logs

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 100

Page size (newest first, ordered by timestamp)

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Echoes a query back through LEDGR, with potential AI commentary

Authorizations:
bearerAuth
query Parameters
query
required
string non-empty

Non-blank query text to echo

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Ledger

Satirical, READ-ONLY personal ledger — the caller's realized P&L (averageCost basis), volume, trade counts, and the bureaucratic FORM GTX-1040-∞ tax return. No money moves.

The caller's satirical read-only ledger summary

Realized P&L (averageCost basis), gross volume, buy/sell counts, and LEDGR's "anomalies detected", computed from the caller's executed trades. READ-ONLY — no money moves. The uid comes only from the bearer principal; the read is bounded to the most recent trades, and meta.truncated flags when that window was capped.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

FORM GTX-1040-∞ — the caller's satirical read-only tax return

The gloriously bureaucratic Galactic Taxation Bureau return, rendered from the same bounded ledger summary as /ledger/summary: labelled line items, a DISPLAY-ONLY assessed levy (never charged), and LEDGR commentary. READ-ONLY — no money moves. Even an empty ledger renders a complete form.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

The caller's open LEDGR insider-trading probes (satirical, read-only)

The satirical anomalies view: every time the caller traded a symbol inside a live Whisper Network window, a READ-ONLY LEDGR insider-trading probe was filed against them. This endpoint reads those probes back — flavour, not enforcement. NO money was ever moved to create a probe, and this read moves none. The uid comes only from the bearer principal; the read is bounded to the newest probes, and meta.truncated flags when that window was capped.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Assess + pay the satirical LEDGR levy on realized trading gains (bearer, atomic)

Assesses + PAYS the mechanical satirical Galactic Speculation Levy on the caller's REALIZED trading gains — the ONE /ledger endpoint that MOVES money (the trading counterpart to the vault early-withdrawal penalty). The levy is DETERMINISTIC from the caller's own executed trades (the averageCost realized P&L) net of a standard deduction + a late-filing surcharge, ceil to integer credits (never rounded to zero), and CLAMPED to the wallet balance (never overdraws; balance ≥ 0 holds). ONE transaction debits the wallet + stamps the per-period audit marker, IDEMPOTENT PER AUDIT PERIOD — a second assess in the same period is a no-op replay that moves NO money (assessed:false). NO double-tax vs the vault : the base is realized trading gains only; vault flows never appear in trades. INCREMENTAL base: the levy is assessed only on the realized gain NEW since the last filing (the delta above the stored high-water mark) — a closed gain is levied EXACTLY ONCE across periods, never re-taxed every period. The uid comes only from the principal; the body is empty (strict).

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Market

Player market reads — stocks, price history, stock news.

List all tradable stocks

Additive screening: the optional q, sector, faction, sort, and order params search/filter/sort the catalog. Filters compose (sector + faction together); sort=change orders by priceChangePercent. When any screening param is present meta.total is the size of the screened result set the caller is paging through.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 100
offset
integer >= 0
Default: 0
q
string

Case-insensitive substring match over symbol and company name.

sector
string

Exact-match sector filter (the trading "industry").

faction
string

Exact-match issuer-faction filter.

sort
string
Enum: "price" "change" "symbol"

Sort key; change sorts on priceChangePercent.

order
string
Default: "asc"
Enum: "asc" "desc"

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get stock details

Authorizations:
bearerAuth
path Parameters
symbol
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get historical prices for a stock

Authorizations:
bearerAuth
path Parameters
symbol
required
string
query Parameters
limit
integer [ 1 .. 500 ]
Default: 30
offset
integer >= 0
Default: 0

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get news related to a specific stock

Authorizations:
bearerAuth
path Parameters
symbol
required
string
Example: VBF

Ticker symbol of the company (e.g., VBF)

query Parameters
limit
integer [ 1 .. 500 ]
Default: 10
offset
integer >= 0
Default: 0

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get the aggregated order-book depth ladder for a stock

Aggregated resting-limit-order depth: pending limit orders bucketed by price into a bid ladder (best/highest first) and an ask ladder (best/lowest first), joined to the stock's current price. AGGREGATE SIZES ONLY — no uids, no per-user data. A level marketable at the current price this tick is flagged. A delisted / pre-listing / unknown symbol returns 404.

Authorizations:
bearerAuth
path Parameters
symbol
required
string
query Parameters
levels
integer [ 1 .. 20 ]
Default: 20

Price levels to show per side (default and ceiling 20).

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Post a market-maker two-sided quote (resting bid + ask)

Places a market-maker two-sided quote on a symbol: a resting bid + ask around the current price as TWO orders/{orderId} docs sharing a quoteId. Each leg is an ordinary limit order — matched by the sim tick UNCHANGED and shown in the depth ladder. A quote is NOT a short: the buy leg still requires funds at fill; the SELL/ASK leg STILL requires held ≥ size, checked at placement AND fill. The quote must straddle the current price (bidPrice < current < askPrice) — a non-straddle or an ask larger than the held position is 422.

Authorizations:
bearerAuth
path Parameters
symbol
required
string
Request Body schema: application/json
required
bidPrice
required
number > 0

The resting bid (buy) limit — must be below the current price.

askPrice
required
number > 0

The resting ask (sell) limit — must be above the current price.

size
required
integer [ 1 .. 100000 ]

Whole shares on each leg.

Responses

Request samples

Content type
application/json
{
  • "bidPrice": 0,
  • "askPrice": 0,
  • "size": 1
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Cancel both legs of a market-maker quote

Cancels every PENDING leg of a market-maker two-sided quote the caller owns. Nested under {symbol} to remove the path ambiguity with /market/{symbol}/history. Already-filled/cancelled legs are left as-is, so a partially-filled quote cancels its remaining side without a 409. An unknown or foreign quoteId — or a quoteId not on {symbol} — returns 404 (no existence leak). The captured spread stays in trade history as normal executions.

Authorizations:
bearerAuth
path Parameters
symbol
required
string
quoteId
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

News

Galaxy market news.

Get all recent market news

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 10
offset
integer >= 0
Default: 0
company
string
Example: company=VBF

Filter to headlines whose impact targets this symbol

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get a specific news article by ID

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Stations

ORBITAXIS station catalog reads.

List all ORBITAXIS stations

Bounded + paginated. Optional limit/offset; meta carries { total, limit, offset }.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get details about a station

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Sectors

Octree-partitioned galactic sectors.

Expand or collapse the sector octree to a target depth

Admin only (mounted on the player router with an in-route admin gate — ). Octree structure is owned exclusively by this endpoint and the seeder; admin sector create/delete via document writes is retired. Expansion updates entity sectorId assignments as sectors split.

Authorizations:
bearerAuth
Request Body schema: application/json
required
action
required
string
Enum: "expand" "collapse"
depth
required
integer [ 0 .. 7 ]

Responses

Request samples

Content type
application/json
{
  • "action": "expand",
  • "depth": 7
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": { }
}

Search sectors by coordinates or filters

Coordinate lookups require x, y, and z together (a partial set is a 400); the lookup level defaults to 3 when level is absent.

Authorizations:
bearerAuth
query Parameters
level
integer [ 0 .. 7 ]

Filter by octree level

x
integer [ 0 .. 7 ]

X coordinate (octant)

y
integer [ 0 .. 7 ]

Y coordinate (octant)

z
integer [ 0 .. 7 ]

Z coordinate (octant)

faction
string

Filter by controlling faction ID

minDanger
integer [ 0 .. 5 ]

Minimum danger level

maxDanger
integer [ 0 .. 5 ]

Maximum danger level

orbitaxisNode
boolean

Filter sectors with ORBITAXIS nodes (true/false only)

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get all child sectors of a parent sector

Authorizations:
bearerAuth
path Parameters
id
required
string
Example: L2-3-2

Parent sector ID

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get the parent sector of a given sector

Authorizations:
bearerAuth
path Parameters
id
required
string
Example: L3-3-2-5

Child sector ID

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List all known Galactic Sectors

Authorizations:
bearerAuth
query Parameters
level
integer [ 0 .. 7 ]

Filter by octree level (depth)

parent
string

Filter by parent sector ID

faction
string

Filter by controlling faction ID

limit
integer >= 1

Maximum number of results (unbounded full listing when absent)

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get a specific Galactic Sector

Authorizations:
bearerAuth
path Parameters
id
required
string
Example: L3-3-2-5

Sector ID (e.g., L0, L1-3, L2-3-2, L3-3-2-5)

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Corporations

Corporation catalog reads.

List all corporations

Optional faction/planet filters that compose. Bounded + paginated: optional limit/offset compose with the filters; meta carries { total, limit, offset } over the filtered result set.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

faction
string
planet
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get details of a specific corporation

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Planets

Planet catalog reads.

Get all planets within the economic network

Bounded + paginated. Optional limit/offset; meta carries { total, limit, offset }.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get data for a specific planet

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Factions

Faction catalog reads.

List all galactic factions

Bounded + paginated. Optional limit/offset; meta carries { total, limit, offset }.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get the weekly faction-war competitive standings

The COSMETIC faction-war competitive frame. Read-only: serves the two sim-maintained factionStandings pointer docs — current (this week's live ranked standings by executed trade volume + the window) and lastWeek (the previous week's frozen ranked result + winner). The standings carry NO price/mechanical consequence and NO PII (faction id/name/volume/rank only). Before the first sim tick both pointers are null (a 200, never a 404). Declared before /factions/{id} so standings is not read as a faction id.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get the caller's faction reputation with every faction

The caller's OWN faction reputation. Read-only, strictly per-owner (keyed by the bearer token uid): for each seeded faction it returns the player's reputation points, the derived TIER, that tier's settlement-fee-discount multiplier, and its access level. Reputation is EARNED by trading a faction's listings and buys a BOUNDED fee discount (applied inside the settlement, clamped — never a credit) plus gated ACCESS to requiredFactionTier stocks. A faction the player has never traded reports the base neutral tier at reputation 0. Leaks no other player's data, no PII. Declared before /factions/{id} so reputation is not read as a faction id.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get detailed info about a faction

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Banking

Bank catalog reads.

Get list of available intergalactic banks

Bounded + paginated. Optional limit/offset; meta carries { total, limit, offset }.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Achievements

Achievement catalog joined with the caller's unlock state.

Get the achievement catalog with the caller's unlock state

Bounded + paginated. Optional limit/offset page the catalog; the caller's unlock state is merged onto the page. meta carries { total, limit, offset } over the catalog.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get a specific achievement by ID

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Messages

System-wide messages and the per-user inbox.

Get public system-wide messages

Bounded + paginated. Optional limit/offset; meta carries { total, limit, offset } over the system-message set.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get a specific public system message by ID

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Mark a public message as read for the caller

Records the read receipt in users/{uid}/read_messages — never on the message document itself.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get the authenticated user's inbox messages

Bounded + paginated, newest first. Optional limit/offset; meta carries { total, limit, offset } over the caller's inbox.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get a specific message from the user's inbox

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Mark an inbox message as read

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Rewards

Daily/monthly reward info and claims.

View today's daily reward

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

View monthly reward status

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Claim today's daily reward

Transactional and exactly-once; an already-claimed or not-yet-eligible reward answers 429 (the standard TooManyRequests error envelope). On success the 200 RewardClaimResult carries nextEligibleClaim.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Claim this month's reward

Transactional and exactly-once; an already-claimed or not-yet-eligible reward answers 429 (the standard TooManyRequests error envelope). On success the 200 RewardClaimResult carries nextEligibleClaim.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Trade

The core player verb. side is buy|sell and type is market|limit — a recorded deviation from the earlier draft of this spec, which overloaded type as buy|sell before limit orders were in scope.

Place a trade — market (settles now) or limit (rests)

The core player verb. Market orders settle synchronously in one atomic transaction at the stock's current simulated price and answer 200 with the TradeRecord. Limit orders rest as a pending order (no debit, no share escrow) matched GTC by the per-minute simulation tick and answer 201 with the order document. Fills are all-or-nothing; no shorting; 24/7 market. A settlement fee may be charged on the fill — the wallet bank's clamped transactionFee × notional, surfaced on the returned TradeRecord (omitted when zero). The optional Idempotency-Key header makes placement replay-safe: a replayed key returns the stored response instead of re-settling.

Authorizations:
bearerAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters ^[A-Za-z0-9_.:-]+$

Optional replay protection: the server stores the response snapshot under {uid}:{key} inside the settlement transaction; a replayed key returns the stored response instead of re-settling (24h TTL). An invalid key is a 400 with source.parameter: "Idempotency-Key". Requests without the header are processed normally.

Request Body schema: application/json
required
symbol
required
string non-empty

Non-blank stock symbol

side
required
string
Enum: "buy" "sell"
type
string
Default: "market"
Enum: "market" "limit" "stop_loss" "take_profit"
quantity
required
integer [ 1 .. 1000000 ]

Whole shares; per-order cap 1,000,000

priceLimit
number or null > 0

Required iff type=limit; forbidden on every other kind

triggerPrice
number or null > 0

Required iff type=stop_loss or take_profit; forbidden on market/limit orders. A stop_loss fills on a downward cross (price <= triggerPrice); a take_profit on an upward cross (price >= triggerPrice). Both settle as a market fill at the current price.

short
boolean or null

Explicit SHORT-SELL intent. Only valid on a side: "sell" market/limit order. When true the sell opens/extends a BORROWED negative position — gated on a margin-enabled account (else 400/margin_required) AND borrow availability (shortAvailable ≥ quantity, else 400/borrow_unavailable) AND initial-margin collateral. Rejected 400/short_on_long if the wallet already holds the symbol long (sell the long first, then short — ; a short may not net/flip a long into a short in one order, the mirror of cover being all-or-nothing). Absent/false = an ordinary sell that still enforces held ≥ quantity. A buy against an existing short auto-covers (this flag is never set on a buy).

Responses

Request samples

Content type
application/json
{
  • "symbol": "string",
  • "side": "buy",
  • "type": "market",
  • "quantity": 1,
  • "priceLimit": 0,
  • "triggerPrice": 0,
  • "short": true
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get the caller's pending limit orders

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get the caller's trade history

Executions only, newest first — pending/cancelled states live on the order documents (GET /trade/orders), never here. The side filter replaces the earlier draft's type query parameter.

Authorizations:
bearerAuth
query Parameters
symbol
string non-empty
side
string
Enum: "buy" "sell"
limit
integer [ 1 .. 100 ]
Default: 25
offset
integer >= 0
Default: 0

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Cancel the caller's own pending limit order

Sets status: "cancelled", cancelledAt, and cancelReason: "user_cancelled" in one transaction. A missing order and another player's order return the same 404 — no existence leak. Executed/cancelled orders are terminal → 409.

Authorizations:
bearerAuth
path Parameters
id
required
string

The order ID

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Wallet

The caller's wallet and settlement history.

Get the caller's wallet and portfolio

Serves the caller's wallets/{uid} doc. Each holding's currentPrice is read-joined live from stocks at request time, with computed marketValue and unrealizedPnL per holding plus portfolio totals. bank is expanded to the Bank catalog doc (degrades to {code} if the catalog doc is missing).

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

View the caller's settlement history

The caller's TradeRecords from trades (which includes reward credits only insofar as they change the balance — the records themselves are trade executions), newest first, paged.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 100 ]
Default: 25
offset
integer >= 0
Default: 0

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Switch the caller's wallet to a different bank

Re-homes the caller's wallets/{uid} bank reference , which changes the settlement fee and interest rate the wallet is subject to going forward. ONE transaction updates BOTH wallets.bank and users.preferredBank so they can never disagree. The target bank must resolve to a banks/{code} catalog doc (the FK — an unknown code is 404). Switching to the wallet's current bank is a no-op success (idempotent). v1 charges no switching fee. The uid comes only from the principal — a caller can only switch their own wallet.

Authorizations:
bearerAuth
Request Body schema: application/json
required
bank
required
string non-empty

Responses

Request samples

Content type
application/json
{
  • "bank": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get the caller's reinvestment settings

The caller's opt-in reinvestment settings off their wallets/{uid} doc: drip (symbols opted into Dividend ReInvestment) and autoInvest (standing scheduled-buy rules, each with its sim-owned lastRunAt marker). A wallet that opted into neither returns empty arrays. Strictly per-owner (uid from the principal). Missing wallet → 404.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Opt a symbol into Dividend ReInvestment (DRIP)

Opts a symbol into DRIP: when a dividend later credits this symbol, the sim reinvests it into as many WHOLE shares as the payout affords via settlement (residual cash stays in the wallet). The {symbol} is FK-validated against the stock universe (stocks) — an unknown symbol is a 422; an add that would exceed the per-user cap is a 422. A re-opt is an idempotent 200. Missing wallet → 404. The symbol is upper-cased server-side. Strictly per-owner.

Authorizations:
bearerAuth
path Parameters
symbol
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Opt a symbol out of DRIP

Removes a symbol from the caller's DRIP list. Future dividends on the symbol land as cash again. The {symbol} is upper-cased to match how it was stored. A symbol not opted into DRIP is a 404. Success is a 204 with no body.

Authorizations:
bearerAuth
path Parameters
symbol
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Opt a bond into coupon auto-reinvestment

Opts a BOND into coupon auto-reinvestment — the DRIP analogue for bonds. When a coupon later credits this bond, the sim buys as many WHOLE units as the coupon affords via settlement (residual cash stays in the wallet). The {symbol} is FK-validated against the stock universe and must be a bond (stocks/{symbol}.kind == "bond") — an unknown symbol or a non-bond is a 422; an add exceeding the per-user cap is a 422. A re-opt is an idempotent 200. Missing wallet → 404. The symbol is upper-cased server-side. Strictly per-owner.

Authorizations:
bearerAuth
path Parameters
symbol
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Opt a bond out of coupon auto-reinvestment

Removes a bond from the caller's coupon-reinvest list. Future coupons on the bond land as cash again. The {symbol} is upper-cased to match how it was stored. A symbol not opted in is a 404. Success is a 204 with no body.

Authorizations:
bearerAuth
path Parameters
symbol
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Set (or replace) a scheduled auto-invest rule

Sets/replaces a standing scheduled-buy rule keyed by {id}. The body is a strict {symbol, amount, cadence} — the rule id is the path, never the body. Each cadence the sim executes a market buy of as many WHOLE shares as amount affords via settlement, skipping (with an inbox notice) on insufficient funds. The symbol is FK-validated against the stock universe (422 if unknown); a NEW rule id that would exceed the per-user cap is a 422. A re-PUT with the same id REPLACES the rule in place and resets its cadence (drops lastRunAt, so it is due next tick). Missing wallet → 404. Strictly per-owner.

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
symbol
required
string non-empty
amount
required
number > 0
cadence
required
string
Enum: "hourly" "daily" "weekly"

Responses

Request samples

Content type
application/json
{
  • "symbol": "string",
  • "amount": 0,
  • "cadence": "hourly"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Remove a scheduled auto-invest rule

Removes the standing scheduled-buy rule with id {id}. The order stops running. A rule id not on the wallet is a 404. Success is a 204 with no body.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Get the bank-products catalog + the caller's product state

The config-driven bank-products catalog: the term-deposit products (short/medium/long — each a termMs lock and a rateBonus on the bank's base interest) and the account-tier definitions (standard/silver/gold — each a rateMult interest bonus and a feeMult fee discount, applied within the clamps), plus the early-withdrawal penalty fraction. Also returns the caller's current tier (defaulting to standard) and their OPEN termDeposits. Kept off the hot GET /wallet read. Strictly per-owner (uid from the principal). Missing wallet → 404.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Open a term deposit

Opens a term deposit: locks amount credits for the chosen product's term at a rate FROZEN at open time (bank base interest × the product bonus × the caller's tier mult). ONE transaction debits amount from the wallet balance and appends the deposit to termDeposits[]. The id is a caller-chosen slug (the withdraw-path handle); a re-open with an already-open id is an idempotent 200 (never debits twice). Insufficient funds, or the per-wallet deposit cap, is a 422. amount below the minimum principal is a 400. Missing wallet → 404. Strictly per-owner.

Authorizations:
bearerAuth
Request Body schema: application/json
required
id
required
string [ 1 .. 64 ] characters
product
required
string
Enum: "short" "medium" "long"
amount
required
number

The principal to lock (≥ MIN_TERM_DEPOSIT_PRINCIPAL)

Responses

Request samples

Content type
application/json
{
  • "id": "string",
  • "product": "short",
  • "amount": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Withdraw a term deposit early

Withdraws a term deposit EARLY: credits principal × (1 − penalty) back to the wallet balance and removes the deposit from termDeposits[] — no interest is earned on an early exit; the forfeited penalty slice leaves the wallet (a sink in the ledger reconciliation). ONE transaction. A deposit id that is not OPEN (never existed, or already matured — the sim credits + removes a matured deposit — or already withdrawn) is a 404; a second withdraw of the same id is therefore a 404 (idempotent). Missing wallet → 404. Strictly per-owner.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Opt the caller's wallet into or out of margin

Opts the caller's wallet into or out of MARGIN — the SCOPE gate for the balance/funds reversal. {enabled: true} sets marginEnabled: true (idempotent; a first enable initializes marginLoan: 0), letting later buys draw on buying power beyond cash (creating a loan). {enabled: false} is refused with 409/loan_outstanding while a loan is still owed (a borrower cannot escape the debt by disabling margin), and with 409/short_outstanding while an open SHORT is held ( — a short is a borrowed negative position that may only ever live on a margin wallet; cover it first). No money moves here — the loan is drawn later by a margin buy. ONE transaction. Missing wallet → 404. Strictly per-owner.

Authorizations:
bearerAuth
Request Body schema: application/json
required
enabled
required
boolean

Responses

Request samples

Content type
application/json
{
  • "enabled": true
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Set the caller's account tier

Sets the caller's account tier to one of standard/silver/gold. The tier modulates the wallet's effective interest rate (bonus) and settlement fee (discount) within the clamps (a discount can never push the fee negative). ONE transaction. Setting the current tier is an idempotent 200. v1 charges no tier fee. Missing wallet → 404. Strictly per-owner.

Authorizations:
bearerAuth
Request Body schema: application/json
required
tier
required
string (AccountTier)
Enum: "standard" "silver" "gold"

Account tier — modulates the wallet's effective interest rate + settlement fee within the clamps. standard is the neutral baseline.

Responses

Request samples

Content type
application/json
{
  • "tier": "standard"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

IPO

Primary-market IPO subscriptions (IPO section). A pre-listing stock opens a subscription window; a player escrows quantity × offerPrice + fee into the book-build, oversubscription is pro-rated at the close, and the sim allocates + lists the stock at the listing tick. A pre-listing symbol rejects ordinary /trade orders with 409/not_yet_listed.

Subscribe to a primary-market IPO (escrow into the book-build)

Subscribe to a PRE-LISTING stock's IPO during its open subscription window (IPO section) — the PRIMARY market. Escrows quantity × offerPrice + fee from the caller's wallet balance in one atomic transaction into an ipoSubscriptions doc; the offer price, the window, and the fee (the wallet bank's clamped transactionFee, tier-adjusted — the same rate settlement charges) are read SERVER-SIDE, never trusted from the client. Oversubscription is pro-rated at the listing close. The optional Idempotency-Key header makes the escrow replay-safe (a replayed key returns the stored subscription without re-escrowing). A re-subscribe REPLACES the caller's prior active subscription for the symbol (its escrow is refunded first, so a re-subscribe is never a double-charge). At the listing tick the sim allocates shares at the offer price + refunds unallocated escrow and flips the stock tradable.

Authorizations:
bearerAuth
path Parameters
symbol
required
string non-empty

The pre-listing stock's ticker (its stocks/{symbol} id).

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters ^[A-Za-z0-9_.:-]+$

Optional replay protection: the server stores the response snapshot under {uid}:{key} inside the settlement transaction; a replayed key returns the stored response instead of re-settling (24h TTL). An invalid key is a 400 with source.parameter: "Idempotency-Key". Requests without the header are processed normally.

Request Body schema: application/json
required
quantity
required
integer >= 1

Whole shares subscribed for (positive; capped at the order ceiling).

Responses

Request samples

Content type
application/json
{
  • "quantity": 1
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Options

Options — cash-settled calls & puts. Buy-to-open a LONG call/put on an equity at a strike + expiry; the premium is priced server-side and debited atomically. LONG-ONLY (no writing/selling — that would reverse the no-short invariant). CAPPED RISK: the buyer's max loss is the premium. At expiry the sim cash-settles the payoff (call max(0, spot − strike) × qty, put max(0, strike − spot) × qty) exactly once; open positions surface in the wallet + net worth at mark-to-market.

Buy a LONG cash-settled option (call or put)

Buy-to-open a LONG cash-settled call or put on an equity — the first derivative, LONG-ONLY and additive (it does NOT reverse 's no-shorting / balance≥0 invariants). The PREMIUM is priced SERVER-SIDE from the underlying's spot, the strike, the option kind, and the time-to-expiry (a deterministic intrinsic + sqrt-time-value model, ceilinged to whole credits) — never trusted from the client, which sends no price. It is debited from the wallet balance in ONE atomic transaction that opens (or folds into) an optionsPositions doc. The optional Idempotency-Key header makes the debit replay-safe. CAPPED RISK: the buyer's maximum loss is the premium — no option path can drive the balance negative. At expiryTick the sim cash-settles the contract (call max(0, spot − strike) × qty, put max(0, strike − spot) × qty, credited; worthless → removed) exactly once. There is NO writing/selling endpoint — long-only keeps the no-short invariant intact.

Authorizations:
bearerAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters ^[A-Za-z0-9_.:-]+$

Optional replay protection: the server stores the response snapshot under {uid}:{key} inside the settlement transaction; a replayed key returns the stored response instead of re-settling (24h TTL). An invalid key is a 400 with source.parameter: "Idempotency-Key". Requests without the header are processed normally.

Request Body schema: application/json
required
symbol
required
string non-empty

The underlying equity ticker (must be a tradable equity — an index or bond is rejected as not optionable).

kind
required
string
Enum: "call" "put"

The option kind — a call (right to the upside) or a put (the downside).

strike
required
number >= 0

The strike price in credits (must be positive — a non-positive value is a 400).

expiryTick
required
integer >= 1

The absolute epoch-ms settlement instant; must be strictly in the future (a past instant is a 400).

quantity
required
integer >= 1

Contracts to buy (positive; 1 contract references 1 underlying share).

Responses

Request samples

Content type
application/json
{
  • "symbol": "string",
  • "kind": "call",
  • "strike": 0,
  • "expiryTick": 1,
  • "quantity": 1
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Index

The index PRIMARY market (create/redeem section). Assemble index units out of the underlying constituent basket (/index/{symbol}/create) or tear held units back into the basket (/index/{symbol}/redeem) — one atomic, value-conserving, idempotency-keyed swap at the index NAV ± cash-in-lieu for fractional constituent shares. Index instruments themselves trade through the ordinary /trade path (they are kind: "index" stocks/{symbol} docs).

Assemble index units from the constituent basket (primary market)

Create units shares of an index instrument by surrendering the underlying constituent basket the caller already holds ( create/redeem section) — the index PRIMARY market. In ONE atomic transaction the handler reads the index's sim-maintained NAV + its constituents' current prices (never trusted from the client), removes floor(units × f[i]) WHOLE shares of each constituent (where f[i] = weight[i] / Σ weight is the value-normalized per-unit basket), charges the fractional-remainder cash-in-lieu at each constituent's price, and mints units index shares at the NAV cost basis. UNIT CONSERVATION holds exactly: the value surrendered (in-kind + cash-in-lieu) equals units × NAV, so no shares or credits are minted for free. The creation REQUIRES the full whole-share basket (400/insufficient_basket otherwise) and enough cash for the cash-in-lieu (400/insufficient_funds). The swap writes a market-BUY TradeRecord on the index symbol (it appears in /wallet/history) and unlocks the index-assembler achievement on the first create. The optional Idempotency-Key header makes the swap replay-safe (a replay returns the stored result without re-swapping). No shorting: every constituent surrender is a funded, whole-share, non-negative rewrite.

Authorizations:
bearerAuth
path Parameters
symbol
required
string non-empty

The index instrument's ticker (a kind: index stocks/{symbol} doc id).

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters ^[A-Za-z0-9_.:-]+$

Optional replay protection: the server stores the response snapshot under {uid}:{key} inside the settlement transaction; a replayed key returns the stored response instead of re-settling (24h TTL). An invalid key is a 400 with source.parameter: "Idempotency-Key". Requests without the header are processed normally.

Request Body schema: application/json
required
units
required
integer >= 1

Whole index units to assemble (create) / disassemble (redeem); capped at the order ceiling.

Responses

Request samples

Content type
application/json
{
  • "units": 1
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Redeem index units back into the constituent basket (primary market)

Redeem units held shares of an index instrument, distributing the underlying constituent basket back to the caller ( create/redeem section) — the inverse of create. In ONE atomic transaction the handler reads the index's sim-maintained NAV + its constituents' current prices, burns units held index shares (only up to held — 400/insufficient_units otherwise), pushes floor(units × f[i]) WHOLE shares of each constituent into the wallet, and credits the fractional-remainder cash-in-lieu at each constituent's price. UNIT CONSERVATION holds exactly: the value returned (in-kind + cash-in-lieu) equals units × NAV. The swap writes a market-SELL TradeRecord on the index symbol (it appears in /wallet/history). The optional Idempotency-Key header makes the swap replay-safe. No shorting: only held units are burned, and no position ever goes negative.

Authorizations:
bearerAuth
path Parameters
symbol
required
string non-empty

The index instrument's ticker (a kind: index stocks/{symbol} doc id).

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters ^[A-Za-z0-9_.:-]+$

Optional replay protection: the server stores the response snapshot under {uid}:{key} inside the settlement transaction; a replayed key returns the stored response instead of re-settling (24h TTL). An invalid key is a 400 with source.parameter: "Idempotency-Key". Requests without the header are processed normally.

Request Body schema: application/json
required
units
required
integer >= 1

Whole index units to assemble (create) / disassemble (redeem); capped at the order ceiling.

Responses

Request samples

Content type
application/json
{
  • "units": 1
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Travel

Distance-priced planet movement. The caller pays BASE_FARE + RATE × octreeDistance(origin.sector, destination.sector) × dangerMultiplier(destination.sector) to move users.currentPlanet; the debit and the move settle in one transaction. Location grants no perks in v1 — the fare is a credit sink and the fiction of travel.

Quote the fare to a destination planet

Prices a trip from the caller's users.currentPlanet to the to planet WITHOUT charging or moving anything. The returned cost is the exact fare POST /v1/travel will debit for the same trip — cost = BASE_FARE + RATE × distance × dangerMultiplier, where distance is the octree hop count between the two planets' sectors and dangerMultiplier scales with the destination sector's dangerLevel. Guards mirror the charge so a quote never promises a trip the POST would refuse.

Authorizations:
bearerAuth
query Parameters
to
required
string non-empty

The destination planets/{id} doc id to price.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Read the caller's current-location home-turf perk

Reports the pilot's current destination perk deterministically for their users.currentPlanet. The perk is the HOME-TURF settlement-fee break: standing on a planet whose resident faction issues a stock discounts that stock's trade fee by a bounded multiplier through the settlement path — never a credit, never a surcharge. This read exposes the homeFaction, the fee feeMultiplier (in [0, 1]), and the derived feeDiscount (1 - feeMultiplier) so a client can show the break without placing a trade. Read-only, PII-free (only the caller's own location). homeFaction/currentPlanet are null when the pilot has no resolvable current planet.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Travel to another planet for a distance-priced fare

Moves the caller to destination and debits the fare in ONE transaction: the wallet balance debit and the users.currentPlanet update settle together, so a player is never charged without arriving nor arrives without paying. The fare is the same travelCost GET /v1/travel/quote reports. The origin is always the caller's own users.currentPlanet (never the body); the caller can only move themselves. Location grants no perks in v1.

Authorizations:
bearerAuth
Request Body schema: application/json
required
destination
required
string non-empty

The destination planets/{id} doc id to travel to.

Responses

Request samples

Content type
application/json
{
  • "destination": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Leaderboard

The sim-computed net-worth leaderboard. The bearer read returns the maintained leaderboard/top board (top 20 by net worth, uid included); the redacted public-dashboard variant is GET /public/leaderboard.

The sim-computed net-worth leaderboard (bearer)

Serves the maintained leaderboard/top doc the sim tick writes each cycle — the top 20 players by net worth (balance + Σ holdings × current price). The board is global: every authenticated player sees the same rankings, and — unlike the redacted public dashboard read (GET /public/leaderboard) — each entry INCLUDES the player uid. Before the sim has ever computed a board the doc is absent, so an empty entries array with computedAt: null is returned (a 200, never a 404).

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

The permanent Hall of Fame — closed leaderboard seasons (bearer)

The permanent Hall of Fame — a bounded, paginated list of the CLOSED leaderboard seasons the sim snapshotted at each season boundary, NEWEST season first (seasonNumber descending). Each record is a season's FROZEN final top board plus its [startsAt, endsAt) window and closedAt stamp. Prestige-only history — no credits, no mechanical effect. Before any season has closed the list is empty (a 200, never a 404). limit/offset are strict (default limit = the read ceiling; the cap bounds worst-case reads however many seasons accrue). Entries carry uid (a bearer read, like GET /leaderboard).

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Vault

The Galactic Retirement Vault — a SEGREGATED, tax-advantaged long-horizon account that makes the display-only LEDGR tax MECHANICAL for vault flows. POST /vault/open dates the maturity clock; POST /vault/contribute moves credits from the wallet into the vault (atomic, conserved); growth accrues tax-deferred into the vault (the sim); POST /vault/withdraw releases funds — an EARLY withdrawal levies a real % penalty (the mechanical tax), at/after maturity there is none. GET /vault reads the vault + the DISPLAY-ONLY deferred-liability statement. Vault funds are not spendable via /trade and are counted once in net worth.

The caller's Galactic Retirement Vault (bearer)

The caller's SEGREGATED, tax-advantaged Galactic Retirement Vault plus the DISPLAY-ONLY deferred-liability statement — the FORM GTX-1040-∞ deferred-tax line for accrued tax-deferred growth (SHOWN, never charged). Vault funds are separate from the spendable wallet and NOT tradable via /trade. The uid comes only from the principal. A caller with no vault → 404 (open one first).

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Open the caller's vault (bearer, idempotent)

Opens the caller's Galactic Retirement Vault — dates the maturity clock (maturesAt = openedAt + VAULT_MATURITY_MS) and creates an EMPTY vault (balance 0). Opening moves NO money. A re-open of an existing vault is an idempotent 200 (the existing doc, no clock reset). The uid comes only from the principal.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Contribute credits into the vault (bearer, atomic)

Moves credits FROM the spendable wallet INTO the segregated vault — DEBITS the wallet by exactly amount and CREDITS the vault by exactly amount in ONE transaction (CONSERVATION — no mint/burn). Gated on sufficient funds (insufficient → 400; the wallet is never overdrawn). Requires an OPEN vault (404 otherwise). Honors the optional Idempotency-Key header (a replay never re-debits). The uid comes only from the principal.

Authorizations:
bearerAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters ^[A-Za-z0-9_.:-]+$

Optional replay protection: the server stores the response snapshot under {uid}:{key} inside the settlement transaction; a replayed key returns the stored response instead of re-settling (24h TTL). An invalid key is a 400 with source.parameter: "Idempotency-Key". Requests without the header are processed normally.

Request Body schema: application/json
required
amount
required
number

Credits to contribute (> 0). Gated on wallet funds.

Responses

Request samples

Content type
application/json
{
  • "amount": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Withdraw credits from the vault (bearer, atomic)

Releases credits from the vault back to the wallet. The withdrawn amount is capped at the vault balance and must be at least VAULT_MIN_WITHDRAW (10 credits; — the minimum rejects a sub-penalty-granularity drip). BEFORE maturity an early-withdrawal PENALTY (max(1, ceil(rate × amount)) — a % levy that DOES move money, the mechanical LEDGR tax, always ≥ 1 and never rounded down) is levied to the house and the NET credits the wallet; at/after maturity the full amount releases with NO penalty. ONE transaction (vault debit + wallet credit + penalty). Honors the optional Idempotency-Key (a replay never re-levies). The uid comes only from the principal.

Authorizations:
bearerAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters ^[A-Za-z0-9_.:-]+$

Optional replay protection: the server stores the response snapshot under {uid}:{key} inside the settlement transaction; a replayed key returns the stored response instead of re-settling (24h TTL). An invalid key is a 400 with source.parameter: "Idempotency-Key". Requests without the header are processed normally.

Request Body schema: application/json
required
amount
required
number >= 10

Credits to withdraw (≥ 10 = VAULT_MIN_WITHDRAW; capped at the vault balance).

Responses

Request samples

Content type
application/json
{
  • "amount": 10
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Watchlist

The per-user personal watchlist + ticker. Bearer, strictly per-owner: GET /watchlist live-joins the caller's followed symbols into a compact ticker (the entry shape); POST /watchlist adds a symbol (FK-validated, 409 dup, 422 over cap); DELETE /watchlist/{symbol} removes one. PUT/DELETE /watchlist/{symbol}/alert set/clear a price alert on a followed symbol — a sim tick that crosses the target mails a fixed-id inbox notification once per crossing. Consumed with the existing bearer ID token; the headless-device credential is deferred.

The personal watchlist ticker (bearer)

The compact personal ticker: the caller's followed symbols live-joined from the current stocks/{symbol} docs — price plus this tick's change, reusing the public ticker's redacted entry shape. Entries are ordered by symbol ascending; a followed symbol whose stock doc has vanished (delisted) is dropped. The list is strictly per-owner (keyed by the token uid). An empty watchlist returns an empty entries array. computedAt is the ISO stamp of the join (never null — the read computes it now). Consumed with the existing bearer ID token; the headless-device credential is deferred.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Add a symbol to the personal watchlist (bearer)

Adds a stock symbol to the caller's watchlist. The symbol is FK-validated against the stock universe (stocks/{symbol}) — an unknown symbol is a 422. An already-followed symbol is a 409; an add that would exceed the per-user cap (50 symbols) is a 422. The symbol is upper-cased server-side.

Authorizations:
bearerAuth
Request Body schema: application/json
required
symbol
required
string non-empty

Responses

Request samples

Content type
application/json
{
  • "symbol": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Remove a symbol from the personal watchlist (bearer)

Removes a symbol from the caller's watchlist. The {symbol} is upper-cased to match how it was stored. A symbol not on the watchlist is a 404. Success is a 204 with no body.

Authorizations:
bearerAuth
path Parameters
symbol
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Set (arm) a price alert on a followed symbol (bearer)

Sets/arms a price alert on a symbol the caller already follows ( amendment). The body is a strict {direction, target} — notify when the price rises above / falls below the target (a finite price > 0). The {symbol} must already be on the watchlist (404 otherwise). Setting an alert RE-ARMS it (single-fire re-arm is explicit): a sim tick whose fresh price crosses the target fires ONE fixed-id inbox message (alert-{symbol}-{n}) and disarms the alert, so it never fires again until re-armed. firedCount is preserved across a re-set so the inbox ids keep advancing. Additive to GET /watchlist (the alert is stored on the watchlist doc; the ticker payload is unchanged).

Authorizations:
bearerAuth
path Parameters
symbol
required
string
Request Body schema: application/json
required
direction
required
string
Enum: "above" "below"
target
required
number > 0

Responses

Request samples

Content type
application/json
{
  • "direction": "above",
  • "target": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Clear a price alert from a followed symbol (bearer)

Removes the alert from a followed symbol ( amendment). Only the alert field is removed — the symbol stays followed (the doc reverts to its pre-alert {symbol, addedAt}). A symbol with no alert (or not followed) is a 404. Success is a 204 with no body.

Authorizations:
bearerAuth
path Parameters
symbol
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "errors": [
    ]
}

Codex

The in-fiction codex / almanac. A bearer, strictly read-only aggregation over the seeded lore — factions, planets, banks, Orbitaxis stations, corporations (incl. the CEO/motto/founding legend) and sectors (incl. the name/description/resources overlay). GET /codex returns a BOUNDED index of the browsable domains with a live per-domain count (no doc payloads); GET /codex/{domain} pages one domain's entries (the shared limit/offset convention, default = the read ceiling). No single call returns the whole universe unbounded. Additive over existing data.

The in-fiction codex / almanac index (bearer)

Returns a BOUNDED index of the browsable lore domains — factions, planets, banks, Orbitaxis stations, corporations, and sectors — each with a human label, an in-fiction blurb, and a live count (an aggregation read, never the documents themselves). The response size is fixed at the number of registered domains regardless of how large the universe grows; to browse a section the caller pages GET /codex/{domain}.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

One browsable codex section (bearer)

Returns one lore domain's entries, read straight off the seeded collection so the additive corporate-personality lore (ceo/motto/foundingStory) and the sector overlay (name/description/resources) surface automatically. Bounded + paginated with the shared convention: optional limit/offset (default limit = the read ceiling), ordered by document id for stable paging; meta carries { total, limit, offset, domain, label } over the full section. {domain} is one of the index slugs — an unknown but well-formed domain is a 404. Each item's data shape is the domain's existing component (Faction / Planet / Bank / OrbitaxisStation / Corporation / GalacticSector).

Authorizations:
bearerAuth
path Parameters
domain
required
string

The lore section slug: factions, planets, banks, stations, corporations, or sectors.

query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Cosmetics

Cosmetic titles, badges, and prestige ranks. STRICTLY prestige — earned from achievements/tracks, they confer NO credits and NO mechanical advantage (fees, wallet, buying power, order priority all unchanged). Bearer, strictly per-owner. GET /cosmetics returns the caller's earned titles/badges + the equipped title; POST /cosmetics/title equips ONE owned title (or unequips with null). The equipped title also renders (additive) on the leaderboard entry and the self-profile (GET /auth/me).

The caller's earned cosmetic titles/badges + equipped title (bearer)

The caller's earned cosmetic titles/badges and the ONE equipped title. STRICTLY prestige — display labels only, no credits and no mechanical effect. Bearer, strictly per-owner (uid from the principal). A player with no user doc yet reports empty cosmetics (a 200, never a 404). Exposes NO wallet/PII — only the player's own cosmetic labels.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Equip (or unequip) the active cosmetic title (bearer)

Equip ONE active cosmetic title, or unequip with titleId: null. Bearer, strictly per-owner. A title must be OWNED (earned from an achievement) before it can be equipped — an unowned title is rejected 422 (no earn-bypass). STRICTLY cosmetic: equipping moves NO credits and confers NO mechanical advantage. Answers 200 with the refreshed cosmetics projection.

Authorizations:
bearerAuth
Request Body schema: application/json
required
titleId
required
string or null

The title id to equip (must be a non-blank id the caller owns), or null to unequip.

Responses

Request samples

Content type
application/json
{
  • "titleId": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Players

Public, spectatable player profiles. Bearer — any pilot may spectate a peer — but the profile is a BOUNDED, REDACTED, OPT-IN allow-list: username, faction, equipped cosmetic title + badges, and leaderboard rank; holdings appear ONLY when the target owner opted in. Wallet balance, email, raw PII, and a holding's private cost basis are never exposed (review).

A player's bounded, redacted, opt-in public profile (bearer)

A BOUNDED, REDACTED, OPT-IN public profile of any player. Bearer — any authenticated pilot may spectate a peer — but the target is the :uid path param, a SPECTATE view, not the caller's own. The payload is an ALLOW-LIST: username, faction, the equipped cosmetic title + badges (prestige-only display labels), and the joined leaderboard rank. Holdings are HIDDEN unless the target OWNER opted in (showHoldings via POST /profile/privacy) — opt-out ⇒ holdingsVisible:false, holdings:[]. Sensitive fields (wallet BALANCE, EMAIL, raw PII, a holding's private averageCost/unrealizedPnL cost basis) are NEVER exposed (privacy-reviewed). Positions are bounded/paginated (limit/offset, capped) with meta {total, limit, offset}. A :uid with no user doc → 404.

Authorizations:
bearerAuth
path Parameters
uid
required
string

The target player's uid (the users/{uid} doc id).

query Parameters
limit
integer [ 1 .. 50 ]
Default: 50

Max spectate positions to return (default AND ceiling PROFILE_POSITIONS_PAGE_SIZE = 50). Only applies when holdings are visible.

offset
integer >= 0
Default: 0

Number of leading positions to skip (paging).

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Profile

The caller's own profile settings. Bearer, strictly per-owner — POST /profile/privacy sets the owner's holdings-visibility flag governing whether their holdings appear on their public profile. A COSMETIC/privacy setting — no credits, no mechanical effect.

Set the caller's holdings-visibility privacy flag (bearer)

The OWNER sets their own holdings-visibility privacy flag. Bearer, STRICTLY per-owner — the uid comes from the principal only, so a caller can only ever change their OWN privacy, never another player's (the load-bearing over-exposure guard). Body is strict {showHoldings: boolean}. A COSMETIC/privacy setting — it moves NO credits and confers NO mechanical advantage; it only gates whether the owner's OWN holdings appear on their public GET /players/{uid} profile. A caller with no user doc → 404. Idempotent.

Authorizations:
bearerAuth
Request Body schema: application/json
required
showHoldings
required
boolean

When true, the owner's holdings appear (redacted) on their public GET /players/{uid} profile. When false (the default), they are hidden. COSMETIC/privacy only — no mechanical effect.

Responses

Request samples

Content type
application/json
{
  • "showHoldings": true
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Streak

The caller's login/activity streak. Bearer, strictly per-owner. Cosmetic/prestige-only — a consecutive-day counter, NO credits and no mechanical effect. GET /streak counts as activity (it advances/starts the streak for today, idempotent per day) and returns {current, longest, lastActiveDay, todayCounted}.

The caller's login/activity streak (bearer)

The caller's login/activity streak. Cosmetic/ prestige-only — a consecutive-day counter, NO credits and no mechanical effect. Reading the streak COUNTS as activity: this handler advances (or starts) the streak for today via the reset rules — same UTC day is a no-op (idempotent per day), the immediately preceding day increments, any larger gap resets to 1 — then returns the resulting block. Strictly per-owner (keyed by the token uid); leaks no other player's data, no wallet, no PII.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Challenges

The rotating daily-challenge board. Bearer, strictly per-owner progress. Cosmetic/prestige-only — completing a challenge grants the daily-challenger achievement, NO credits and no mechanical advantage. Completion is detected via the existing game-event stream and is idempotent per day. GET /challenges returns the day's board joined with the caller's own completion state.

The rotating daily-challenge board + the caller's progress (bearer)

The day's rotating daily-challenge board joined with the CALLER's own completion state. Cosmetic/prestige-only — completing a challenge grants the daily-challenger achievement, NO credits and no mechanical advantage. The board is the deterministic per-UTC-day selection (the sim maintains a display cache at dailyChallenges/{dayKey}); completion is detected via the existing game-event stream and is idempotent per day. Bounded (≤ the board size). Strictly per-owner: only the caller's own completions are exposed, never another player's; no wallet, no PII.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Events Calendar

The player events calendar — a bearer, read-only aggregation of the upcoming live-ops beats (season closes, faction-war closes, holiday events, today's challenge theme) on the Galactic Standard Calendar. NARRATIVE/COSMETIC: the read changes nothing. Sourced from the REAL sim-owned systems so nothing drifts; bounded to a forward horizon + hard-capped, PII-free.

The upcoming live-ops schedule on the Galactic Standard Calendar (bearer)

The player events calendar — a bearer, READ-ONLY aggregation of the upcoming live-ops beats the sim already schedules, rendered on the Galactic Standard Calendar. NARRATIVE/COSMETIC : reading it changes nothing (no price, wallet, or holding). It merges beats from the REAL sim-owned systems so nothing can drift — the season marker (simulation/leaderboardSeason), the faction-war marker (factionStandings/current), upcoming calendar_events holiday spawns, and today's deterministic daily-challenge theme. BOUNDED to a forward horizon (horizonMs) and hard-capped, sorted soonest-first. Aggregate/PII-free: schedule metadata + display copy only — no wallet balance, email, or uid. Empty before anything is scheduled.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Invites

Recruit-a-trader invitations. Bearer, strictly per-owner. Each player mints a personal recruit code, shares it, and a friend who redeems it is attributed as a referral — ONE referrer per user, self-referral + cycles blocked, all idempotent. When a recruit makes their first trade, BOTH sides earn a tiered COSMETIC referral badge via the achievement engine + inbox mail. STRICTLY prestige — NO credits, fees, buying power, or trading advantage. No PII is exposed: the list returns only the caller's own code, counts, and recruit uids.

Mint (or return) the caller's personal recruit code (bearer)

Mint the caller's personal recruit code, or return the existing one. Bearer, strictly per-owner, RATE-LIMITED. Idempotent: one code per player — a first mint answers 201, a re-mint answers 200 with the SAME code. STRICTLY prestige — a code grants NO credits; the referral badge is cosmetic. Empty body (strict — any field is a 400).

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

The caller's recruit code + referral status (bearer)

The caller's recruit code + referral status. Bearer, strictly per-owner. Read-only (never mints). Returns {code, minted, redeemCount, referralCount, recruits[]} — a never-minted caller sees their deterministic preview code with minted:false and zero counts. PRIVACY: exposes only the caller's own code, counts, and a bounded list of recruit uids — never a wallet balance, email, or another player's private data.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Redeem a recruit code — attribute a referrer (bearer)

Redeem a recruit code to attribute the caller to its owner. Bearer, strictly per-owner, RATE-LIMITED. This is the additive "registration accepts an optional invite code" path — SEPARATE from registration and never blocking the frozen flow. Rules: ONE referrer per user (409 on a second redeem); SELF-referral rejected (422); a CYCLE rejected (422); an unknown code 404; all idempotent. NO credits, badge, or referral-count move here — the mutual COSMETIC badge waits for the recruit's first-trade milestone.

Authorizations:
bearerAuth
Request Body schema: application/json
required
code
required
string

The recruit code to redeem (non-blank).

Responses

Request samples

Content type
application/json
{
  • "code": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Merch

VoidEx Exchange Store. VoidEx LISTS + DEEP-LINKS to a print-on-demand storefront ONLY — it handles no payment, no PII storage, no fulfillment (the hosted personalization page is, out of scope). GET /public/merch is the UNAUTHENTICATED, PII-free, bounded/paginated catalog. GET /merch/{id}/personalize is bearer: it mints a SHORT-LIVED, HMAC-SIGNED deep-link whose opaque, EXPIRING token encodes ONLY the OPT-IN- PER-FIELD data the player chose (display name, top holding, faction, tagline) — never a wallet balance, email, or uid. STRICTLY cosmetic — a shirt confers no trading advantage. The signing secret comes from Secret Manager, never the repo/client.

VoidEx Exchange Store catalog (public)

The merch catalog — admin-managed cosmetic config, listed + deep-linked ONLY (VoidEx handles no payment/PII/fulfillment). Unauthenticated and PII-free: it reads only the static merch collection, never any player/wallet/token data. BOUNDED + PAGINATED (limit/offset; meta carries { total, limit, offset }). personalizable: true items accept a signed deep-link from GET /merch/{id}/personalize (bearer).

query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Mint a signed personalize deep-link (bearer)

Mint a SHORT-LIVED SIGNED deep-link for an authenticated player. Personalization is OPT-IN PER FIELD: the query names which of displayName/topHolding/faction/tagline to include; a field not opted in is ABSENT from the token — nothing is exposed without consent (privacy-reviewed). The personalizeUrl carries an HMAC-signed, EXPIRING opaque token encoding ONLY those opt-in display fields — never a wallet balance, email, or uid (cosmetic only). The signing secret comes from Secret Manager (never hardcoded). Only a personalizable: true item mints a link (404 unknown, 422 not personalizable).

Authorizations:
bearerAuth
path Parameters
id
required
string

The merch catalog item id.

query Parameters
displayName
string
Enum: "true" "false"

Opt in to embed the player's display name.

topHolding
string
Enum: "true" "false"

Opt in to embed the player's top LONG holding symbol.

faction
string
Enum: "true" "false"

Opt in to embed the player's faction id.

tagline
string <= 60 characters

Opt in to embed a caller-supplied cosmetic tagline.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Syndicates

Player-formed trading syndicates / guilds. Found/join/leave a syndicate (BOUNDED membership; one-per-pilot), pool credits into a shared, CONSERVED treasury (contribute is any member; withdraw is OWNER-only; both are atomic wallet↔treasury transfers that never mint, burn, or leak), and climb a sim-aggregated syndicate leaderboard. All collections are server-only + default-deny; reads redact PII.

List trading syndicates (bearer)

A BOUNDED list of trading syndicates — each row carries public facts only (id, name, tag, memberCount, treasuryBalance, createdAt); no member PII.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Found a trading syndicate (bearer)

FOUND a syndicate. The caller becomes the OWNER; ONE transaction creates the guild doc (treasury 0, memberCount 1), the owner membership edge, and claims the unique tag marker. Rejected 409 if the caller already belongs to any syndicate (one-per-pilot) or the tag is taken. The uid comes only from the principal.

Authorizations:
bearerAuth
Request Body schema: application/json
required
name
required
string

Display name (3-40 chars after trim).

tag
required
string

Short roster tag (2-5 alphanumeric chars, uppercased; unique).

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "tag": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

The syndicate net-worth leaderboard (bearer)

The sim-maintained syndicate leaderboard — a bounded aggregation of member net worth + treasury per syndicate, ranked. Returns the maintained board, or an empty board before the first tick. No PII.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

The caller's own syndicate (bearer)

The caller's OWN syndicate + role, or {syndicate: null, role: null} when unaffiliated. Strictly per-owner — the uid comes only from the principal.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

A syndicate's detail + roster (bearer)

One syndicate's detail + a BOUNDED, REDACTED member roster. Each roster row carries the member's display username + public prestige fields only (uid, username, role, joinedAt, contributedTotal) — no email, no wallet balance.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Join a syndicate (bearer)

JOIN a syndicate. ONE transaction adds the member edge + increments the count. BOUNDED — rejected 409 syndicate_full at the member cap. One-per-pilot — rejected 409 already_member if in a different syndicate. Idempotent: re-joining the SAME syndicate is a no-op success (no count inflation).

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Leave a syndicate (bearer)

LEAVE a syndicate. Removes the caller's edge + decrements the count. The OWNER cannot leave while others remain (409 owner_must_transfer); the last member (owner) leaving DISBANDS — but only if the treasury is EMPTY (409 treasury_not_empty otherwise, so credits never vanish). Leaving never moves treasury into a member's wallet.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Contribute credits into the shared treasury (bearer, atomic)

Move credits FROM the caller's wallet INTO the shared treasury. ONE transaction DEBITS the wallet by exactly amount and CREDITS the treasury by exactly amount (CONSERVATION — no mint/burn; wallet never overdrawn). Requires membership (403 not_a_member). Honors the optional Idempotency-Key (a replay never re-debits).

Authorizations:
bearerAuth
path Parameters
id
required
string
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters ^[A-Za-z0-9_.:-]+$

Optional replay protection: the server stores the response snapshot under {uid}:{key} inside the settlement transaction; a replayed key returns the stored response instead of re-settling (24h TTL). An invalid key is a 400 with source.parameter: "Idempotency-Key". Requests without the header are processed normally.

Request Body schema: application/json
required
amount
required
integer

Credits to contribute (> 0). Gated on wallet funds.

Responses

Request samples

Content type
application/json
{
  • "amount": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Withdraw credits from the shared treasury (bearer, owner-only, atomic)

OWNER-only: move credits OUT of the shared treasury back to the OWNER's wallet. ONE transaction DEBITS the treasury by exactly amount and CREDITS the owner's wallet by exactly amount (CONSERVATION). A non-owner is rejected 403 not_owner (never a leak). Gated on treasury balance (never negative; insufficient → 400). Honors the optional Idempotency-Key.

Authorizations:
bearerAuth
path Parameters
id
required
string
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters ^[A-Za-z0-9_.:-]+$

Optional replay protection: the server stores the response snapshot under {uid}:{key} inside the settlement transaction; a replayed key returns the stored response instead of re-settling (24h TTL). An invalid key is a 400 with source.parameter: "Idempotency-Key". Requests without the header are processed normally.

Request Body schema: application/json
required
amount
required
integer

Credits to withdraw (> 0). Gated on the treasury balance.

Responses

Request samples

Content type
application/json
{
  • "amount": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Tournaments

PvP trading tournaments — an ISOLATED paper-wallet arena. Enroll to receive an EQUAL, FICTIONAL paper wallet, trade the same live market prices against the PAPER store ONLY while the tournament is active, and rank by paper net worth at close. The load-bearing invariant is REAL-ECONOMY ISOLATION: a tournament trade NEVER touches the real wallet/holdings/settlement, and no path converts paper credits into real credits (prizes are cosmetic prestige only). All collections are server-only + default-deny; reads redact PII.

List PvP tournaments (bearer)

A BOUNDED list of PvP trading tournaments — each row carries the schedule + status + entryCount; no entrant PII (the paper wallets live in a separate collection).

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

The caller's own tournament entries / paper wallets (bearer)

The caller's OWN tournament entries — a bounded per-owner scan (uid from the principal only). Each row is the ISOLATED paper wallet plus its computed paperNetWorth. Declared before /{id} so mine is not swallowed as an id.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

One tournament's detail (bearer)

One tournament's schedule + status detail. Read-only; no entrant PII. 404 if unknown.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

The caller's own paper wallet for a tournament (bearer)

The caller's OWN paper wallet for a tournament. Strictly per-owner (uid from the principal). Returns {entry: null} if not enrolled, else the ISOLATED paper wallet + its computed paperNetWorth. Standard API envelope.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

The frozen results of a closed tournament (bearer)

The FROZEN ranking snapshot of a closed tournament — each row carries a display username + uid (the public spectate key) + paper net worth; no email/real wallet (redaction). 404 before the tournament closes.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Enroll in a tournament — mint an isolated paper wallet (bearer)

Enroll in a tournament, minting the caller's ISOLATED PAPER WALLET (paperBalance = startingBalance, empty holdings) + bumping the entrant count. Allowed only while the tournament is enrolling (409 otherwise). BOUNDED by the entrant cap (409 tournament_full). IDEMPOTENT: a re-enroll returns the existing entry (200) with NO re-mint/re-count/reset. The stake is FICTIONAL — NO real credit is debited (REAL-ECONOMY ISOLATION).

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Place a paper trade against the isolated paper wallet (bearer)

A paper trade against the caller's ISOLATED PAPER WALLET. Reads the paper entry + the live stocks/{symbol} price and writes ONLY the entry doc — NEVER the real wallet/holdings/settlement/trades (the load-bearing REAL-ECONOMY ISOLATION invariant). Allowed only while the tournament is active (409 otherwise). Fills at the current real price; NO fees, NO shorting, NO margin. Insufficient paper funds/shares → 400.

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
symbol
required
string
side
required
string
Enum: "buy" "sell"
quantity
required
integer

Whole shares (> 0).

Responses

Request samples

Content type
application/json
{
  • "symbol": "string",
  • "side": "buy",
  • "quantity": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Admin

The complete game-state write surface. Every /admin route requires the admin role — bearer auth plus the role: "admin" custom claim; non-admins receive an enveloped 403 before rate limiting. Verb model is POST (create) / PUT (partial update) / DELETE — no PATCH (the frozen CORS method list excludes it). Creates on client-keyed IDs answer 409 on duplicates; PUT is replay-idempotent; DELETE of an absent resource is 404. Validation failures answer 400 with one entry per zod issue, source.pointer in the /data/attributes/<field> form.

Expand or collapse the sector octree to a target depth

Admin only (mounted on the player router with an in-route admin gate — ). Octree structure is owned exclusively by this endpoint and the seeder; admin sector create/delete via document writes is retired. Expansion updates entity sectorId assignments as sectors split.

Authorizations:
bearerAuth
Request Body schema: application/json
required
action
required
string
Enum: "expand" "collapse"
depth
required
integer [ 0 .. 7 ]

Responses

Request samples

Content type
application/json
{
  • "action": "expand",
  • "depth": 7
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": { }
}

List users (admin)

User records joined with profile and wallet data; admin is computed from the role custom claim. Paged by cursor pageToken (meta carries total, limit, pageToken, hasMore).

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 1000 ]
Default: 50
pageToken
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Get a user (admin)

Authorizations:
bearerAuth
path Parameters
uid
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a user's profile document (admin)

Updates the user's profile document only — privileges live solely in the role custom claim (POST /admin/users/{uid}/role); role-shaped fields are rejected as unknown keys.

Authorizations:
bearerAuth
path Parameters
uid
required
string
Request Body schema: application/json
required
non-empty
username
string
email
string
faction
string
homePlanet
string
currentPlanet
string
preferredBank
string

Responses

Request samples

Content type
application/json
{
  • "username": "string",
  • "email": "string",
  • "faction": "string",
  • "homePlanet": "string",
  • "currentPlanet": "string",
  • "preferredBank": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": { }
}

Delete a user (admin)

Authorizations:
bearerAuth
path Parameters
uid
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Grant or revoke the admin role (admin)

The audited, guarded admin-claim path. "admin" merges role: "admin" into the target's custom claims; "player" removes the role key, preserving every other claim. Two 409 guards: you cannot change your own role, and the last remaining admin cannot be demoted. The claim rides the ID token — it takes effect on the target's next token refresh.

Authorizations:
bearerAuth
path Parameters
uid
required
string
Request Body schema: application/json
required
role
required
string
Enum: "admin" "player"

admin merges role: "admin" into the custom claims; player removes the role key, preserving all other claims

Responses

Request samples

Content type
application/json
{
  • "role": "admin"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List stock documents (admin)

Authorizations:
bearerAuth
query Parameters
limit
integer >= 1
Default: 100
offset
integer >= 0
Default: 0

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a stock (admin)

symbol is the doc ID; a duplicate answers 409 (retry-safe create). Server-owned createdAt/updatedAt and sim-owned price bookkeeping are rejected on input.

Authorizations:
bearerAuth
Request Body schema: application/json
required
symbol
required
string non-empty

Non-blank; becomes the doc ID

name
string
sector
string
faction
string
planet
string
description
string
price
number > 0
Default: 100
volatility
number > 0
Default: 0.1
sharesOutstanding
integer >= 1
Default: 1000000
analystRating
string
Enum: "strong_buy" "buy" "hold" "sell" "strong_sell"

The syndicate analyst rating. Admin-writable; also seeded + occasionally sim-updated (an admin write wins). Drives a clamped, decaying drift bias toward priceTarget in the price walk.

priceTarget
number > 0

The analyst price target the rating leans price toward. Admin-writable; positive.

Responses

Request samples

Content type
application/json
{
  • "symbol": "string",
  • "name": "string",
  • "sector": "string",
  • "faction": "string",
  • "planet": "string",
  • "description": "string",
  • "price": 100,
  • "volatility": 0.1,
  • "sharesOutstanding": 1000000,
  • "analystRating": "strong_buy",
  • "priceTarget": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a stock document (admin)

Authorizations:
bearerAuth
path Parameters
symbol
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a stock (admin)

Authorizations:
bearerAuth
path Parameters
symbol
required
string
Request Body schema: application/json
required
non-empty
name
string
sector
string
faction
string
planet
string
description
string
price
number > 0
volatility
number > 0
sharesOutstanding
integer >= 1
analystRating
string
Enum: "strong_buy" "buy" "hold" "sell" "strong_sell"

The syndicate analyst rating. Admin-writable.

priceTarget
number > 0

The analyst price target. Admin-writable; positive.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "sector": "string",
  • "faction": "string",
  • "planet": "string",
  • "description": "string",
  • "price": 0,
  • "volatility": 0,
  • "sharesOutstanding": 1,
  • "analystRating": "strong_buy",
  • "priceTarget": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a stock (admin)

The player-money soft exception: stock deletion never cascades into player data (wallets.holdings[]/trades keep the symbol), but the delete is 409-blocked while any corporations.stockSymbols[] still names the ticker — re-point the issuer first.

Authorizations:
bearerAuth
path Parameters
symbol
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List calendar events (admin)

Authorizations:
bearerAuth
query Parameters
limit
integer >= 1
Default: 100
offset
integer >= 0
Default: 0
active
boolean

Filter by active flag

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a calendar event (admin)

start/expires cross the wire as ISO-8601 strings and are persisted as UTC datetimes on the server. expires defaults server-side to start + 24h. The doc ID is server-generated (autoId).

Authorizations:
bearerAuth
Request Body schema: application/json
required
name
required
string non-empty
type
required
string
Enum: "system_update" "celestial_event" "station_event" "planetary_event" "outpost_event" "faction_event" "conflict" "celebration" "artifact_discovery" "void_breach" "mission_window" "anomaly_detected" "verification_reset" "sync_cycle" "access_key_rotation"
start
required
string <date-time>
expires
string <date-time>

Server default when omitted — start + 24h

description
string
Default: ""
active
boolean
Default: true
object

Optional market weather for the event

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "type": "system_update",
  • "start": "2019-08-24T14:15:22Z",
  • "expires": "2019-08-24T14:15:22Z",
  • "description": "",
  • "active": true,
  • "effect": {
    }
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a calendar event (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a calendar event (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
non-empty
name
string non-empty
type
string
Enum: "system_update" "celestial_event" "station_event" "planetary_event" "outpost_event" "faction_event" "conflict" "celebration" "artifact_discovery" "void_breach" "mission_window" "anomaly_detected" "verification_reset" "sync_cycle" "access_key_rotation"
start
string <date-time>
expires
string <date-time>
description
string
active
boolean
object or null

A valid object sets/replaces the effect; null clears it; absence leaves it untouched

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "type": "system_update",
  • "start": "2019-08-24T14:15:22Z",
  • "expires": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "active": true,
  • "effect": {
    }
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a calendar event (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List wallet documents (admin)

Authorizations:
bearerAuth
query Parameters
limit
integer >= 1
Default: 50
offset
integer >= 0
Default: 0

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Update a wallet document (admin)

The governed replacement for direct wallet surgery — validated, enveloped, logged. Partial update of bank, balance, holdings; the uid doc-id mirror is rejected.

Authorizations:
bearerAuth
path Parameters
uid
required
string
Request Body schema: application/json
required
non-empty
bank
string non-empty
balance
number >= 0
Array of objects (Holding)

Responses

Request samples

Content type
application/json
{
  • "bank": "string",
  • "balance": 0,
  • "holdings": [
    ]
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get simulation status (admin)

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": { }
}

Trigger a simulation tick (admin)

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get aggregate telemetry (admin)

The ops-facing, AGGREGATE-ONLY telemetry read-back. Serves the bounded telemetry/aggregate counters (fixed kind × cohort key space — never grows with player count or time) folded into per-kind + per-cohort totals + a grand total. PII-free (counts only; no uid, wallet, or email — admin-only). Empty + stable-shaped before any tracked action. Telemetry is invisible to players and NEVER alters trading fairness.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List station documents (admin)

read-back parity — the admin surface previously had create/update/delete but no read for this domain. Bounded + paginated; ordered by document id.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a station (admin)

409 on either a duplicate client-keyed id OR a sector already occupied by another station (sectors.orbitaxisNode is 1:1-unique, — the derived back-link is maintained transactionally with the create).

Authorizations:
bearerAuth
Request Body schema: application/json
required
id
required
string non-empty
name
required
string
sectorId
required
string
type
required
string
status
required
string
description
required
string
required
object
controlledBy
string
services
Array of strings

Additive OPTIONAL station services directory.

economyBlurb
string

Additive OPTIONAL in-fiction economy blurb.

residentPosture
string

Additive OPTIONAL resident-faction posture.

Responses

Request samples

Content type
application/json
{
  • "id": "string",
  • "name": "string",
  • "sectorId": "string",
  • "type": "string",
  • "status": "string",
  • "description": "string",
  • "coordinates": {
    },
  • "controlledBy": "string",
  • "services": [
    ],
  • "economyBlurb": "string",
  • "residentPosture": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a station document (admin)

read-back parity — the raw station document, or 404.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a station (admin)

Re-pointing sectorId to a sector already occupied by another station is a 409 (sectors.orbitaxisNode is 1:1-unique — the old sector's back-link is cleared and the new one set transactionally).

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
non-empty
name
string
sectorId
string
type
string
status
string
description
string
object
controlledBy
string
services
Array of strings

Additive OPTIONAL station services directory.

economyBlurb
string

Additive OPTIONAL in-fiction economy blurb.

residentPosture
string

Additive OPTIONAL resident-faction posture.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "sectorId": "string",
  • "type": "string",
  • "status": "string",
  • "description": "string",
  • "coordinates": {
    },
  • "controlledBy": "string",
  • "services": [
    ],
  • "economyBlurb": "string",
  • "residentPosture": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a station (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a sector's content fields (admin)

Content fields only: name, description, classification, dangerLevel, controllingFaction. The DERIVED orbitaxisNode has LEFT this request body — it is the station-maintained 1:1 back-link of stations.sectorId and is rejected here like any unknown key. Octree structure (level, parent, coordinates, the ID) is owned by POST /sectors/manage-depth and never admin-writable; there is deliberately no admin sector POST or DELETE. The :id is validated against the octree ID grammar.

Authorizations:
bearerAuth
path Parameters
id
required
string

Octree sector ID (e.g. L2-3-2)

Request Body schema: application/json
required
non-empty
name
string
description
string
classification
string
dangerLevel
integer [ 0 .. 5 ]
controllingFaction
string or null

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "description": "string",
  • "classification": "string",
  • "dangerLevel": 5,
  • "controllingFaction": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List corporation documents (admin)

read-back parity. Bounded + paginated; ordered by document id.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a corporation (admin)

Authorizations:
bearerAuth
Request Body schema: application/json
required
id
required
string non-empty
name
required
string
founded
required
string
faction
required
string
description
required
string
headquarters
string
planets
Array of strings
Default: []
stockSymbols
Array of strings
Default: []
ceo
string
motto
string
foundingStory
string

Responses

Request samples

Content type
application/json
{
  • "id": "string",
  • "name": "string",
  • "founded": "string",
  • "faction": "string",
  • "description": "string",
  • "headquarters": "string",
  • "planets": [ ],
  • "stockSymbols": [ ],
  • "ceo": "string",
  • "motto": "string",
  • "foundingStory": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a corporation document (admin)

read-back parity — the raw corporation document, or 404.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a corporation (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
non-empty
name
string
founded
string
faction
string
description
string
headquarters
string
planets
Array of strings
stockSymbols
Array of strings
ceo
string
motto
string
foundingStory
string

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "founded": "string",
  • "faction": "string",
  • "description": "string",
  • "headquarters": "string",
  • "planets": [
    ],
  • "stockSymbols": [
    ],
  • "ceo": "string",
  • "motto": "string",
  • "foundingStory": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a corporation (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List planet documents (admin)

read-back parity. Bounded + paginated; ordered by document id.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a planet (admin)

Authorizations:
bearerAuth
Request Body schema: application/json
required
id
required
string non-empty
name
required
string
sectorId
required
string
type
required
string
faction
required
string
description
required
string
atmosphere
string
gravity
number
climate
string
terrain
string
population
string
orbit
string
notableFeatures
Array of strings
services
Array of strings

Additive OPTIONAL planet services directory.

economyBlurb
string

Additive OPTIONAL in-fiction planet-economy blurb.

residentPosture
string

Additive OPTIONAL resident-faction posture.

Responses

Request samples

Content type
application/json
{
  • "id": "terra-prime",
  • "name": "string",
  • "sectorId": "string",
  • "type": "string",
  • "faction": "string",
  • "description": "string",
  • "atmosphere": "string",
  • "gravity": 0,
  • "climate": "string",
  • "terrain": "string",
  • "population": "string",
  • "orbit": "string",
  • "notableFeatures": [
    ],
  • "services": [
    ],
  • "economyBlurb": "string",
  • "residentPosture": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a planet document (admin)

read-back parity — the raw planet document, or 404.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a planet (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
non-empty
name
string
sectorId
string
type
string
faction
string
description
string
atmosphere
string
gravity
number
climate
string
terrain
string
population
string
orbit
string
notableFeatures
Array of strings
services
Array of strings

Additive OPTIONAL planet services directory.

economyBlurb
string

Additive OPTIONAL in-fiction planet-economy blurb.

residentPosture
string

Additive OPTIONAL resident-faction posture.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "sectorId": "string",
  • "type": "string",
  • "faction": "string",
  • "description": "string",
  • "atmosphere": "string",
  • "gravity": 0,
  • "climate": "string",
  • "terrain": "string",
  • "population": "string",
  • "orbit": "string",
  • "notableFeatures": [
    ],
  • "services": [
    ],
  • "economyBlurb": "string",
  • "residentPosture": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a planet (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List faction documents (admin)

read-back parity. Bounded + paginated; ordered by document id. Surfaces the server-stamped createdAt/updatedAt and the system-maintained associatedBanks/associatedCorporations views.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a faction (admin)

Authorizations:
bearerAuth
Request Body schema: application/json
required
id
required
string non-empty
name
required
string non-empty
description
required
string
headquarters
required
string
influence
required
number

Responses

Request samples

Content type
application/json
{
  • "id": "string",
  • "name": "string",
  • "description": "string",
  • "headquarters": "string",
  • "influence": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a faction document (admin)

read-back parity — the raw faction document, or 404.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a faction (admin)

Partial update. The DERIVED associatedBanks/associatedCorporations have LEFT the request body — they are system-maintained and rejected here like any unknown key; a partial update preserves them untouched.

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
non-empty
name
string non-empty
description
string
headquarters
string
influence
number

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "description": "string",
  • "headquarters": "string",
  • "influence": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a faction (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List bank documents (admin)

read-back parity. Bounded + paginated; ordered by document id (the bank code). Surfaces the server-stamped createdAt/updatedAt.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a bank (admin)

code is uppercased server-side and becomes the doc ID; reads supply it back from the doc ID.

Authorizations:
bearerAuth
Request Body schema: application/json
required
code
required
string non-empty
name
required
string non-empty
description
required
string
interestRate
required
number >= 0
transactionFee
required
number >= 0
traits
Array of strings
Default: []
faction
string
planet
string
autoAudit
boolean

Responses

Request samples

Content type
application/json
{
  • "code": "string",
  • "name": "string",
  • "description": "string",
  • "interestRate": 0,
  • "transactionFee": 0,
  • "traits": [ ],
  • "faction": "string",
  • "planet": "string",
  • "autoAudit": true
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a bank document (admin)

read-back parity — the raw bank document, or 404. The :code path param is uppercased into the doc ID, so the read is case-insensitive like the write paths.

Authorizations:
bearerAuth
path Parameters
code
required
string

Bank code (doc ID)

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a bank (admin)

Authorizations:
bearerAuth
path Parameters
code
required
string

Bank code (doc ID)

Request Body schema: application/json
required
non-empty
name
string non-empty
description
string
interestRate
number >= 0
transactionFee
number >= 0
traits
Array of strings
faction
string
planet
string
autoAudit
boolean

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "description": "string",
  • "interestRate": 0,
  • "transactionFee": 0,
  • "traits": [
    ],
  • "faction": "string",
  • "planet": "string",
  • "autoAudit": true
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a bank (admin)

Authorizations:
bearerAuth
path Parameters
code
required
string

Bank code (doc ID)

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List catalog achievement documents (admin)

read-back parity. The catalog only — per-user unlock state (users/{uid}/achievements) is never surfaced here. Bounded + paginated; ordered by document id.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a catalog achievement (admin)

The catalog only — the per-user unlock state (users/{uid}/achievements) is never writable here; unlocked/ unlockedAt fail as unknown keys.

Authorizations:
bearerAuth
Request Body schema: application/json
required
id
required
string non-empty
title
required
string non-empty
description
required
string
type
required
string
Enum: "milestone" "secret" "seasonal" "event"
points
required
integer >= 0
icon
string
reward
string

Responses

Request samples

Content type
application/json
{
  • "id": "string",
  • "title": "string",
  • "description": "string",
  • "type": "milestone",
  • "points": 0,
  • "icon": "string",
  • "reward": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a catalog achievement document (admin)

read-back parity — the raw achievement document, or 404.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a catalog achievement (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
non-empty
title
string non-empty
description
string
type
string
Enum: "milestone" "secret" "seasonal" "event"
points
integer >= 0
icon
string
reward
string

Responses

Request samples

Content type
application/json
{
  • "title": "string",
  • "description": "string",
  • "type": "milestone",
  • "points": 0,
  • "icon": "string",
  • "reward": "string"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a catalog achievement (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List system message documents (admin)

read-back parity. The messages/{id} broadcast catalog only — per-user read receipts live in users/{uid}/read_messages and are not surfaced here. Bounded + paginated; ordered by document id.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a system message (admin)

Read receipts (read/readAt) live in users/{uid}/read_messages and are rejected here as unknown keys. received defaults server-side to time of processing.

Authorizations:
bearerAuth
Request Body schema: application/json
required
id
required
string non-empty
subject
required
string non-empty
sender
required
string non-empty
body
required
string non-empty
category
required
string
Enum: "system" "bank" "anomaly" "puzzle" "reward"
gcDate
string

Free-form galactic-calendar label

received
string <date-time>

Responses

Request samples

Content type
application/json
{
  • "id": "string",
  • "subject": "string",
  • "sender": "string",
  • "body": "string",
  • "category": "system",
  • "gcDate": "string",
  • "received": "2019-08-24T14:15:22Z"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a system message document (admin)

read-back parity — the raw message document, or 404.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a system message (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
non-empty
subject
string non-empty
sender
string non-empty
body
string non-empty
category
string
Enum: "system" "bank" "anomaly" "puzzle" "reward"
gcDate
string
received
string <date-time>

Responses

Request samples

Content type
application/json
{
  • "subject": "string",
  • "sender": "string",
  • "body": "string",
  • "category": "system",
  • "gcDate": "string",
  • "received": "2019-08-24T14:15:22Z"
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a system message (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List news headline documents (admin)

read-back parity. Bounded + paginated; ordered newest-first by timestamp (matching the player /v1/news read). Surfaces the sim-owned applied/appliedAt consumption markers the player read may omit.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a news headline (admin)

id is optional — absent means a server-generated autoId (matching the sim generator); present makes the create replay-safe via the 409-on-duplicate guard. A headline with impact is consumed exactly once by the simulation; the sim-owned applied/appliedAt markers are rejected on input.

Authorizations:
bearerAuth
Request Body schema: application/json
required
id
string non-empty
title
required
string non-empty
body
required
string non-empty
source
required
string non-empty
timestamp
string <date-time>
object
tags
Array of strings
Default: []

Responses

Request samples

Content type
application/json
{
  • "id": "string",
  • "title": "string",
  • "body": "string",
  • "source": "string",
  • "timestamp": "2019-08-24T14:15:22Z",
  • "impact": {
    },
  • "tags": [ ]
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a news headline document (admin)

read-back parity — the raw news document, or 404.

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a news headline (admin)

Updating impact on an already-applied headline does NOT re-apply it — applied stays true and only the simulation may clear it.

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
non-empty
title
string non-empty
body
string non-empty
source
string non-empty
timestamp
string <date-time>
object
tags
Array of strings

Responses

Request samples

Content type
application/json
{
  • "title": "string",
  • "body": "string",
  • "source": "string",
  • "timestamp": "2019-08-24T14:15:22Z",
  • "impact": {
    },
  • "tags": [
    ]
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a news headline (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

List reward definitions (admin)

read-back parity — this closes the gap-audit HIGH: reward DEFINITIONS previously had no read on ANY surface (create/update/delete only). Bounded + paginated; ordered by document id.

Authorizations:
bearerAuth
query Parameters
limit
integer [ 1 .. 500 ]
Default: 500

Page size. Optional; defaults to the read ceiling (500) so callers that supply no paging receive the full list exactly as before. The ceiling bounds worst-case reads.

offset
integer >= 0
Default: 0

Number of leading results to skip.

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": [
    ]
}

Create a reward definition (admin)

Freezes the portal's de-facto {name, type, amount} shape ( Consequences #4). Nothing in the reward engine reads these yet — claim amounts are hard-coded; wiring definitions in is future work.

Authorizations:
bearerAuth
Request Body schema: application/json
required
id
required
string non-empty
name
required
string non-empty
type
required
string non-empty

Free-form label (daily/monthly/faction-scoped by convention)

amount
required
number >= 0

Responses

Request samples

Content type
application/json
{
  • "id": "string",
  • "name": "string",
  • "type": "string",
  • "amount": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Get a reward definition (admin)

read-back parity — the raw reward definition, or 404. Part of closing the gap-audit HIGH (reward definitions had no read on any surface).

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update a reward definition (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string
Request Body schema: application/json
required
non-empty
name
string non-empty
type
string non-empty
amount
number >= 0

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "type": "string",
  • "amount": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Delete a reward definition (admin)

Authorizations:
bearerAuth
path Parameters
id
required
string

Responses

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Update the LEDGR status singleton (admin)

A set-merge upsert — the doc is created when absent and untouched fields survive. Always 200, never 404. The logs subcollection is not writable through this surface.

Authorizations:
bearerAuth
Request Body schema: application/json
required
non-empty
status
string
Enum: "stable" "syncing" "volatile" "offline"
version
string
message
string
uptime
string
lastSynced
string <date-time>
anomaliesDetected
integer >= 0
quantumChannelsOpen
integer >= 0

Responses

Request samples

Content type
application/json
{
  • "status": "stable",
  • "version": "string",
  • "message": "string",
  • "uptime": "string",
  • "lastSynced": "2019-08-24T14:15:22Z",
  • "anomaliesDetected": 0,
  • "quantumChannelsOpen": 0
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}

Schedule a corporate action (admin)

Schedules a value-preserving corporate action that the simulation applies ONCE at its effectiveTick. ships the stock split / reverse-split: at the effective tick every holder's position is rewritten (shares = floor(shares × N/D), averageCost × D/N), the stock price rebases by the same factor, and any fractional residual is paid as cash-in-lieu — all conserving value. The symbol is FK-validated against the stock universe (unknown → 400) and effectiveTick (epoch-milliseconds) must be in the future.

Authorizations:
bearerAuth
Request Body schema: application/json
required
type
required
string

The action type. split = a stock split / reverse-split.

symbol
required
string non-empty

The ticker the action operates on (FK-validated against stocks).

ratio
required
string

The split ratio N:D (numerator:denominator), positive integers, not 1:1. 2:1 doubles shares + halves price (forward); 1:3 thirds shares + triples price (reverse).

effectiveTick
required
integer >= 1

Epoch-milliseconds instant the action fires — must be in the future. The sim applies the action ONCE on the first tick at/after this time.

Responses

Request samples

Content type
application/json
Example
{
  • "type": "split",
  • "symbol": "string",
  • "ratio": "2:1",
  • "effectiveTick": 1
}

Response samples

Content type
application/json
{
  • "statusCode": 0,
  • "meta": { },
  • "links": { },
  • "relationships": { },
  • "data": {
    }
}