> ## Documentation Index
> Fetch the complete documentation index at: https://docs.seesaw.fun/llms.txt
> Use this file to discover all available pages before exploring further.

# Get the key owner's profile, balances and current season



## OpenAPI

````yaml /api-reference/openapi.yaml get /me
openapi: 3.1.0
info:
  title: SeeSaw Open API
  version: 2.2.0
  description: |
    The **open API** of SeeSaw — a dedicated surface for AI agents and
    third-party integrations, exposed both as REST (`/open/v1/*`) and as an
    **MCP server** (`/mcp`, Streamable HTTP, stateless) in the same process.

    Every key can **read**; keys created with the `write` scope can also act
    on the owner's behalf: place a position, publish an opinion, back an
    opinion. Writes always run in two phases — a `preview_*` call returns the
    summary the user is meant to confirm plus a short-lived `confirm_token`,
    and the execute call accepts only that token.

    This contract is independent from the app-facing API:
    it does not inherit app DTO shapes and follows its own conventions.

    ## Conventions
    - Field names are `snake_case`; enums are lowercase strings.
    - Decimals (coins, gems, prices) are **strings with 4 decimal places**
      (`"100.0000"`) — never floats. Rounding for display is the client's call.
      One deliberate exception: `avg_price` (coins per ruby, a ratio rather
      than a price) carries **6 decimal places**, and says so at the field.
    - Timestamps are RFC 3339 UTC (`2026-09-09T12:00:00Z`).
    - Every resource carries a human-visitable `url`.
    - Lists use **opaque cursor pagination**: `{data, next_cursor, has_more}`.
      `limit` defaults to 20, max 100. Reusing a cursor with different query
      params does not guarantee continuity.
    - Single resources are returned bare (no wrapper); errors use
      `{"error": {"code", "message", "details"}}`.
    - Vocabulary follows the 2.0 glossary: **opinion** (not comment), **back**,
      **position**, **stake**, **payout**.

    ## Authentication and scopes
    Every request (public data included) requires a **personal API key** (PAT):
    `Authorization: Bearer sspat_…`. Keys are issued per user (max 5 active)
    and can be revoked anytime. Key management lives on the app API
    (`/v1/users/me/api-keys`, JWT auth), NOT on this surface.

    Two scope tiers, chosen when the key is created and **fixed for the life of
    the key** (there is no upgrade path — create a new key instead):

    | Scopes | Grants |
    | --- | --- |
    | `["read"]` | The whole read surface. The default. |
    | `["read","write"]` | The read surface **plus** the six write operations. |

    A read-only key hitting a write endpoint gets `403 FORBIDDEN_SCOPE`; over
    MCP it never sees the write tools at all.

    ## The write flow
    1. Call `POST …/order-preview`, `…/opinion-preview` or `…/back-preview`.
       Nothing is charged; the response carries a `summary` plus, when the
       action is currently possible, a `confirm_token` and its `expires_at`.
    2. **Show the summary to the user and get explicit confirmation.**
    3. Call `POST /orders`, `/opinions` or `/backs` with only that token.

    Tokens are valid **5 minutes**, are bound to the issuing user, key, action
    and parameters, and are single-use. `place_order` and `back_opinion` are
    idempotent on retry with the same token (`replayed: true`);
    `publish_opinion` is strictly single-use and answers a retry with
    `409 CONFIRM_TOKEN_USED`.

    ## Rate limits (per key)
    - **Read window**: 120 requests/min and 10,000 requests/day across the
      surface. The three `preview_*` operations count only against this window.
    - **Search**: additionally capped at 30 requests/min — a dedicated per-key
      budget shared by REST `/search` and the MCP `search_topics` tool.
    - **Write window**: the three executing operations (`/orders`, `/opinions`,
      `/backs`) are additionally capped at **10/min and 200/day**; they also
      consume the read window.
    - `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset`
      response headers reflect the binding minute window; 429 responses
      carry `Retry-After`. Over MCP, exceeding a quota surfaces as a tool
      error naming the retry delay.

    ## MCP server
    `POST https://api.seesaw.fun/mcp` (Streamable HTTP, stateless, same
    `Authorization` header). The server reports the contract version as its
    `serverInfo.version` (`2.2.0`). A read key sees **12 tools**; a
    read+write key sees **18**:

    - Read: `search_topics`, `list_topics`, `list_topic_categories`,
      `get_topic`, `list_topic_opinions`, `get_my_profile`,
      `list_my_positions`, `list_my_opinions`, `list_my_backed_opinions`,
      `list_my_watchlist`, `list_my_coin_ledger`, `list_my_ruby_ledger`.
    - Write: `preview_order` / `place_order`, `preview_opinion` /
      `publish_opinion`, `preview_back` / `back_opinion`.

    Read and preview tools are annotated `readOnlyHint: true`; the three
    executing tools carry `destructiveHint: true` and
    `_meta["anthropic/requiresUserInteraction"] = true`, which compatible
    clients turn into a mandatory confirmation prompt. Tool results carry the
    corresponding REST response JSON.

    Tools that return titles, options or opinions take an optional `language`
    argument; REST reads the `Accept-Language` header instead (unknown values
    fall back to `en`). Supported: `en` (default), `zh-TW`, `zh-CN`, `vi`,
    `id`, `th`, `ja`, `ko`, `ms`. Over MCP, aliases such as `zh` or `en-US`
    fold onto the closest supported code and any other value is rejected —
    which is why the tool schemas carry no JSON-schema `enum` for it.

    ## Trading mechanisms
    2.0 topics run one of two mechanisms. `v2_share` topics are legacy
    share-pool markets — readable, but not tradable through this surface
    (`preview_order` answers `MECHANISM_UNSUPPORTED`). `v21_lmsr` is the live
    one; list and search endpoints default to it.

    - **Stake rules are fixed per topic.** Each `v21_lmsr` topic freezes its
      own stake range, market depth and rules version when it is created;
      they never change afterwards, and a later platform change only affects
      new topics. The range is published as the topic's `stake_rules`
      (`min_coins`..`max_coins`, whole coins; older topics are 100..100).
    - **The user chooses the stake.** `preview_order` takes `amount_coins`
      within that range. If the user stated an amount, pass exactly that;
      if they did not, ask them — an agent must never choose or infer an
      amount. An amount outside the range is refused with
      `STAKE_OUT_OF_RANGE`, never adjusted. Omitting `amount_coins` stakes
      `stake_rules.default_coins`, a fallback rather than a suggestion.
    - **One order per topic, no fees.** A user places at most one order on a
      topic; the whole stake buys units.
    - **Opening prices.** A topic can open at initial probabilities set when
      it is published, rather than at an even split across its options;
      read the current `implied_price` instead of assuming 1/n.
    - **Payout.** A correct option pays `units × ruby multiplier` **rubies**;
      the stake itself is not returned. The multiplier is 1 unless a ruby
      boost (`ruby_boost` on the topic) is live at one of three moments: when
      the order is placed, when the topic settles, or (in the app) when the
      position is sold — whichever gives the highest multiplier applies. The
      order records the multiplier live when it is placed; a boost live at
      settlement raises it. So every `payout_if_correct_rubies` figure is a
      **guaranteed minimum** (units × the order's current multiplier), not
      the final amount.
    - **Selling is app-only.** Users can sell a whole position for rubies in
      the SeeSaw app; this surface offers no sell operation. A sold position
      shows `sold: true` with `sold_rubies`, and is never settled or refunded
      afterwards — `won`, `payout_rubies` and `payout_if_correct_rubies`
      stay null. Selling still counts as the topic's one order.

    ## Rubies and the retired sapphire
    Sapphires were merged 1:1 into rubies; opinion paybacks now pay rubies.
    `list_my_ruby_ledger` shows every ruby movement — settlement payouts,
    positions sold in the app, opinion paybacks and the one-off conversion
    of the old sapphire balance. The sapphire-named fields remain on the
    wire with the same values as their `ruby_*` replacements and are marked
    `deprecated`; they will be removed in contract **3.0.0**:

    | Deprecated | Use instead |
    | --- | --- |
    | `MyProfile.sapphires` (always `"0.0000"`) | `rubies` |
    | `sapphire_payback` | `ruby_payback` |
    | `sapphire_total` | `ruby_total` |
    | `max_relay_payback_sapphires` | `max_relay_payback_rubies` |
servers:
  - url: https://api.seesaw.fun/open/v1
    description: Production
security:
  - apiKey: []
tags:
  - name: Topics
    description: 2.0 prediction topics — browse, search, detail, opinions
  - name: Me
    description: Data of the authenticated key owner
  - name: Trading
    description: Place a position (preview then execute, `write` scope)
  - name: Opinions
    description: Publish and back opinions (preview then execute, `write` scope)
paths:
  /me:
    get:
      tags:
        - Me
      summary: Get the key owner's profile, balances and current season
      operationId: getMyProfile
      responses:
        '200':
          description: The authenticated user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MyProfile'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  schemas:
    MyProfile:
      type: object
      description: |
        The authenticated key owner — the only user shape carrying balances.
        Coins are the 2.0 currency, spent on positions and backing. Rubies and
        emeralds are reward assets: rubies from correct positions (including
        any ruby boost), positions sold in the SeeSaw app, and opinion
        paybacks; emeralds from invites. `sapphires` is deprecated — see its
        field.
      required:
        - id
        - handle
        - nickname
        - avatar_url
        - url
        - coin_balance
        - rubies
        - sapphires
        - emeralds
        - created_at
        - current_season
      properties:
        id:
          type: string
          format: uuid
        handle:
          type:
            - string
            - 'null'
        nickname:
          type: string
        avatar_url:
          type:
            - string
            - 'null'
        url:
          type: string
        coin_balance:
          type: string
          description: Decimal string, 4 places.
        rubies:
          type: string
          description: |
            Ruby balance. Every movement is listed by
            `GET /me/ruby-ledger`.
        sapphires:
          type: string
          deprecated: true
          description: |
            **Deprecated — removed in 3.0.0.** Always `"0.0000"`: sapphires
            were merged 1:1 into `rubies`, and opinions now pay rubies.
        emeralds:
          type: string
        created_at:
          type: string
          format: date-time
        current_season:
          description: |
            The running season and the key owner's ruby score in it. Added in
            2.2.0. `null` when it could not be read, never a false zero.
          oneOf:
            - $ref: '#/components/schemas/CurrentSeason'
            - type: 'null'
    CurrentSeason:
      type: object
      description: |
        The running ranking season. Read the window from here: a season can
        span several calendar months.
      required:
        - code
        - label
        - name
        - starts_at
        - ends_at
        - rubies
      properties:
        code:
          type: string
          description: |
            Stable, opaque season id, e.g. `2026-09`. Do not derive dates from
            it: use `starts_at` and `ends_at`.
        label:
          type: string
          description: Display label, e.g. `S1`.
        name:
          type: string
          description: Season name set by SeeSaw.
        starts_at:
          type: string
          format: date-time
          description: Window start, inclusive.
        ends_at:
          type: string
          format: date-time
          description: Window end, exclusive.
        rubies:
          type: string
          description: |
            The key owner's ruby score this season, the same number as on the
            SeeSaw app's rank page. Decimal string, 4 places. It is not the
            ruby balance (`rubies` on the profile), and it is not the sum of
            the `range=season` ledger rows: position payouts count toward the
            season the order was placed in, while the ledger window goes by
            booking time, and some ledger rows do not count toward the score.
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - UNAUTHORIZED
                - FORBIDDEN
                - FORBIDDEN_SCOPE
                - NOT_FOUND
                - OPINION_NOT_FOUND
                - INVALID_ARGUMENT
                - RATE_LIMITED
                - SERVICE_DISABLED
                - INTERNAL
                - CONFIRM_TOKEN_INVALID
                - CONFIRM_TOKEN_EXPIRED
                - CONFIRM_TOKEN_USED
                - CONFIRM_UNAVAILABLE
                - MECHANISM_UNSUPPORTED
                - PREVIEW_UNAVAILABLE
                - TOPIC_ALREADY_STAKED
                - TOPIC_CLOSED
                - TOPIC_QUOTE_EXPIRED
                - TOPIC_QUOTE_INVALID
                - TOPIC_MECHANISM_MISMATCH
                - STAKE_OUT_OF_RANGE
                - RETRY_IDEMPOTENCY_KEY
                - OPINION_ALREADY_BACKED
                - OPINION_BACK_PRICE_MOVED
                - OPINION_BACK_RETRY
                - OPINION_BACK_FULL
                - OPINION_BACK_CHAIN_BROKEN
                - OPINION_SIDE_REQUIRED
                - OPINION_SIDE_INVALID
                - OPINION_TOPIC_CLOSED
                - OPINION_TOPIC_HIDDEN
                - OPINIONS_UNAVAILABLE
                - INSUFFICIENT_BALANCE
                - CONTENT_INVALID
                - CONTENT_MODERATED
                - USER_PUNISHED
              description: |
                Stable contract values. `SERVICE_DISABLED` (503) is the
                emergency kill switch; `FORBIDDEN_SCOPE` (403) means the key
                lacks the `write` scope. The `CONFIRM_TOKEN_*` and rule codes
                only occur on the write surface. `OPINION_SIDE_REQUIRED` /
                `OPINION_SIDE_INVALID` are rare but reachable: `preview_opinion`
                already answers a missing or invalid side with
                `can_publish: false` (reason `SIDE_REQUIRED`), so they surface
                as a 400 from `publish_opinion` only when the owner's position
                changed between preview and publish (e.g. no position at
                preview, then an order on the other side before publishing) and
                the side in the token no longer fits.
            message:
              type: string
            details:
              type: object
              description: >-
                Optional context, e.g. `{"field": "limit"}`, `{"retry_after":
                12}` or `{"required_scope": "write"}`.
              additionalProperties: true
  responses:
    Unauthorized:
      description: >-
        Missing, invalid, revoked, or expired API key (carries
        `WWW-Authenticate`)
      headers:
        WWW-Authenticate:
          schema:
            type: string
          description: Bearer realm="seesaw-open-api"
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: >-
        Rate limit exceeded (see `Retry-After` and `details.retry_after`
        seconds)
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait before retrying.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: sspat_ personal API key
      description: |
        Personal API key issued per SeeSaw user account:
        `Authorization: Bearer sspat_…`. Manage keys on the app API
        (`/v1/users/me/api-keys`). Revocation propagates within ~60 seconds
        (validation cache window).

        The key's **scopes** decide what it may do: every key has `read`; a key
        created with `["read","write"]` may also use the six write operations
        (marked `x-required-scope: write` in this document). Scopes are fixed at
        creation — to get write access, create a new key. A read-only key
        calling a write operation gets `403 FORBIDDEN_SCOPE`.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.