BlockVectra Me API

Current user, username, and identity linking

Operations 5

GET /me Current username, identity list, and current session #
PATCH /me Update username #
POST /me/identities/siwe Link wallet #
POST /me/identities/oauth Link GitHub or Google identity using login code #
DELETE /me/identities/{identity_id} Unlink identity #

Specifications

Schemas & Data

📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-freshness-envelope-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-token-transfer-list-envelope-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-address-transfer-list-envelope-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-tokens-batch-envelope-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-dex-swap-list-envelope-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-transaction-list-envelope-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-key-view-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-balance-response-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-plans-response-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-resets-response-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-identity-view-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-session-response-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-subscription-create-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-subscription-patch-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-remove-result-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-add-result-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-event-history-page-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-subscription-list-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-faucet-claim-accepted-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-faucet-claim-request-schema.json
📊
JSONSchema
https://artifacts.apis.io/blockvectra/json-schema/blockvectra-json-rpc-response-body-schema.json

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/blockvectra:blockvectra-me-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

blockvectra-me-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: BlockVectra Console Me API
  version: 2.1.2
  description: Console account management APIs for wallet login, identities, API keys, plans, balances, charges, top-ups, usage and reset chances. Product APIs (JSON-RPC, Data API and WebSocket) do not depend on these account management APIs.
servers:
- url: https://console-api.blockvectra.com/v1
tags:
- name: Me
  description: Current user, username, and identity linking
paths:
  /me:
    get:
      tags:
      - Me
      operationId: getMe
      summary: Current username, identity list, and current session
      security:
      - SessionToken: []
      responses:
        '200':
          description: Current user.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MeResponse'
              examples:
                meOk:
                  $ref: '#/components/examples/meOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags:
      - Me
      operationId: updateUsername
      summary: Update username
      description: '`{"username": string | null}`: leading/trailing whitespace stripped, empty string treated as null (clears username), up to 64 Unicode characters, without control characters (Unicode category `Cc`), otherwise 400 `invalid_request` (`reason: invalid_username`). Returns same body as `GET /me` on success. Not unique, no other format constraints.'
      security:
      - SessionToken: []
      requestBody:
        $ref: '#/components/requestBodies/UsernameBody'
      responses:
        '200':
          description: Updated current user (matching `GET /me` shape).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MeResponse'
              examples:
                meUsernameUpdated:
                  $ref: '#/components/examples/meUsernameUpdated'
        '400':
          description: Username exceeds length limit, contains control characters, or unknown fields.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                errInvalidUsername:
                  $ref: '#/components/examples/errInvalidUsername'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /me/identities/siwe:
    post:
      tags:
      - Me
      operationId: linkSiwe
      summary: Link wallet
      description: 'Submit `purpose=link` challenge message (requested in same session) and signature. Request `Origin` mode must match challenge: browser challenge must send same `Origin` (`https://`), programmatic challenge must omit `Origin`, otherwise returns 400 `siwe_invalid` (reason `domain_mismatch`). New link returns 201; returns 200 idempotently if wallet already belongs to this user.


        Scope of support:

        - Supported: EOA wallets on Ethereum mainnet (Chain ID 1).

        - Not supported: Any other chains (Chain ID is embedded by server; submitting modified Chain ID fails byte-for-byte check returning 400 `siwe_invalid`, reason `signature`), contract wallets (EIP-1271), and smart accounts.'
      security:
      - SessionToken: []
      requestBody:
        $ref: '#/components/requestBodies/SiweVerifyBody'
      responses:
        '200':
          description: Wallet already linked to this user (idempotent).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentityView'
              examples:
                linkSiweAlreadyOwn:
                  $ref: '#/components/examples/linkSiweAlreadyOwn'
        '201':
          description: Newly linked identity.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentityView'
              examples:
                linkSiweCreated:
                  $ref: '#/components/examples/linkSiweCreated'
        '400':
          description: Verification failed (including challenge purpose not link, or not requested in this session).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                linkSiweWrongPurpose:
                  $ref: '#/components/examples/linkSiweWrongPurpose'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Wallet already linked to another user, or user already has 5 linked identities.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                linkSiweInUse:
                  $ref: '#/components/examples/linkSiweInUse'
                errIdentityLimitReached:
                  $ref: '#/components/examples/errIdentityLimitReached'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /me/identities/oauth:
    post:
      tags:
      - Me
      operationId: linkOauth
      summary: Link GitHub or Google identity using login code
      description: Exchange an OAuth login code and its PKCE verifier using the current session. Returns 201 for a new identity and 200 when already linked to this user.
      security:
      - SessionToken: []
      requestBody:
        $ref: '#/components/requestBodies/OAuthTokenBody'
      responses:
        '200':
          description: Identity already linked to this user (idempotent).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentityView'
              examples:
                linkOauthAlreadyOwn:
                  $ref: '#/components/examples/linkOauthAlreadyOwn'
        '201':
          description: Newly linked identity.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentityView'
              examples:
                linkOauthCreated:
                  $ref: '#/components/examples/linkOauthCreated'
        '400':
          description: Invalid, reused login code, or verifier mismatch.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                linkOauthCodeReused:
                  $ref: '#/components/examples/linkOauthCodeReused'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Identity already linked to another user, or user already has 5 linked identities.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                linkOauthInUse:
                  $ref: '#/components/examples/linkOauthInUse'
                errIdentityLimitReached:
                  $ref: '#/components/examples/errIdentityLimitReached'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /me/identities/{identity_id}:
    delete:
      tags:
      - Me
      operationId: unlinkIdentity
      summary: Unlink identity
      description: Identity must belong to user and cannot be the sole identity; sessions authenticated with this identity are revoked.
      security:
      - SessionToken: []
      parameters:
      - $ref: '#/components/parameters/IdentityIdPath'
      responses:
        '204':
          description: Unlinked successfully.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Identity does not exist or does not belong to user.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                unlinkNotOwn:
                  $ref: '#/components/examples/unlinkNotOwn'
        '409':
          description: Cannot unlink sole identity.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                unlinkLastIdentity:
                  $ref: '#/components/examples/unlinkLastIdentity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  headers:
    RetryAfter:
      description: Suggested wait time in seconds.
      required: true
      schema:
        type: integer
        minimum: 1
      example: 6
    WwwAuthenticate:
      description: '401 always includes `WWW-Authenticate: Bearer`.'
      required: true
      schema:
        type: string
        const: Bearer
  schemas:
    OAuthTokenRequest:
      type: object
      additionalProperties: false
      required:
      - code
      - code_verifier
      properties:
        code:
          type: string
          pattern: ^rgc_[0-9a-f]{64}$
          description: Single-use login code delivered via callback redirect.
        code_verifier:
          type: string
          pattern: ^[A-Za-z0-9._~-]{43,128}$
          description: PKCE verifier corresponding to `code_challenge` at start (RFC 7636).
    MeResponse:
      type: object
      additionalProperties: false
      required:
      - username
      - created_at
      - identities
      - session
      properties:
        username:
          $ref: '#/components/schemas/Username'
        created_at:
          $ref: '#/components/schemas/Timestamp'
        identities:
          type: array
          items:
            $ref: '#/components/schemas/IdentityView'
        session:
          type: object
          additionalProperties: false
          required:
          - expires_at
          - idle_timeout_secs
          properties:
            expires_at:
              $ref: '#/components/schemas/Timestamp'
            idle_timeout_secs:
              type: integer
    Timestamp:
      type: string
      format: date-time
      pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$
      description: RFC 3339 UTC, second precision, ending in `Z` (`2026-09-26T08:00:00Z`); no fractional seconds, without `+00:00` timezone offsets.
      example: 2026-09-26 08:00:00+00:00
    SiweVerifyRequest:
      type: object
      additionalProperties: false
      required:
      - message
      - signature
      properties:
        message:
          $ref: '#/components/schemas/SiweMessage'
        signature:
          type: string
          pattern: ^0x[0-9a-fA-F]{130}$
          description: 65-byte ECDSA signature (r||s||v).
    Username:
      type:
      - string
      - 'null'
      maxLength: 64
      description: 'Username: populated with default value on first login (GitHub login for GitHub, null for SIWE and Google; SIWE accounts start with no username and clients derive the display from the address); modified via `PATCH /me` only; linking identities does not alter it. Not unique, not queryable. Stripped of leading/trailing whitespace on `PATCH`, empty string treated as null, up to 64 Unicode characters, without control characters (Unicode category `Cc`), otherwise 400 `invalid_request` (`reason: invalid_username`).

        '
    IdentityView:
      type: object
      additionalProperties: false
      required:
      - identity_id
      - provider
      - subject
      - display
      - created_at
      - last_login_at
      properties:
        identity_id:
          type: string
          pattern: ^id_[a-z2-7]{26}$
        provider:
          type: string
          enum:
          - siwe
          - github
          - google
        subject:
          type: string
          description: 'SIWE: `eip155:1:<lowercase address>`; GitHub: numeric id; Google: `sub`.'
        display:
          type:
          - string
          - 'null'
          description: EIP-55 address for SIWE, login for GitHub (display only), null for Google.
        created_at:
          $ref: '#/components/schemas/Timestamp'
        last_login_at:
          oneOf:
          - $ref: '#/components/schemas/Timestamp'
          - type: 'null'
    UsernameRequest:
      type: object
      additionalProperties: false
      required:
      - username
      properties:
        username:
          $ref: '#/components/schemas/Username'
    SiweMessage:
      type: string
      description: EIP-4361 message (LF line endings, no trailing newline), up to 2048 bytes.
      maxLength: 2048
      pattern: '^[a-z0-9.-]+(:[0-9]+)? wants you to sign in with your Ethereum account:\n0x[0-9a-fA-F]{40}\n\n[ -~]+\n\nURI: [!-~]+\nVersion: 1\nChain ID: [0-9]+\nNonce: [0-9a-f]{32}\nIssued At: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z\nExpiration Time: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$'
    ErrorResponse:
      type: object
      additionalProperties: false
      required:
      - error
      properties:
        error:
          type: object
          additionalProperties: false
          required:
          - code
          - message
          properties:
            code:
              type: string
              enum:
              - invalid_request
              - siwe_invalid
              - oauth_invalid
              - login_code_invalid
              - unauthenticated
              - user_disabled
              - not_found
              - provider_disabled
              - method_not_allowed
              - identity_in_use
              - identity_limit_reached
              - last_identity
              - key_not_active
              - key_limit_reached
              - no_reset_available
              - payload_too_large
              - rate_limited
              - signup_rate_limited
              - signup_paused
              - usage_unavailable
              - service_unavailable
              - internal
            message:
              type: string
              description: Human-readable explanation, subject to wording adjustments.
            reason:
              type: string
              enum:
              - expired
              - chain_mismatch
              - domain_mismatch
              - signature
              - active_keys
              - daily_creations
              - invalid_username
              - nothing_to_reset
              - per_ip
              - global
              - cu_cap
              - expires_at
              - audience
              description: 'Machine-readable reason (siwe_invalid: expired, chain_mismatch, domain_mismatch, signature; key_limit_reached: active_keys; rate_limited: daily_creations for 24h key creation limit; invalid_request: invalid_username, cu_cap, expires_at; no_reset_available: nothing_to_reset when balance is already at or above target; signup_rate_limited: per_ip or global).'
            limit:
              type: integer
              description: Limit threshold value (e.g. active_keys limit for key_limit_reached).
  requestBodies:
    SiweVerifyBody:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SiweVerifyRequest'
          examples:
            reqSiweVerify:
              $ref: '#/components/examples/reqSiweVerify'
            reqSiweVerifyUnknownField:
              $ref: '#/components/examples/reqSiweVerifyUnknownField'
    OAuthTokenBody:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthTokenRequest'
          examples:
            reqOauthToken:
              $ref: '#/components/examples/reqOauthToken'
            reqOauthTokenUnknownField:
              $ref: '#/components/examples/reqOauthTokenUnknownField'
    UsernameBody:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UsernameRequest'
          examples:
            reqUsernameSet:
              $ref: '#/components/examples/reqUsernameSet'
            reqUsernameClear:
              $ref: '#/components/examples/reqUsernameClear'
            reqUsernameTooLong:
              $ref: '#/components/examples/reqUsernameTooLong'
  examples:
    reqOauthTokenUnknownField:
      summary: Contains undefined fields
      value:
        code: rgc_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
        code_verifier: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
        redirect_uri: http://127.0.0.1:53682/cb
    errUnauthenticated:
      summary: Invalid session (missing, malformed, unknown, expired, or revoked)
      value:
        error:
          code: unauthenticated
          message: missing or invalid session token
    unlinkNotOwn:
      summary: Identity belongs to another user
      value:
        error:
          code: not_found
          message: the requested resource was not found
    errIdentityLimitReached:
      summary: User already has 5 linked identities
      value:
        error:
          code: identity_limit_reached
          message: a user can have at most 5 identities
    meOk:
      summary: Current user
      value:
        username: null
        created_at: 2026-09-26 08:00:00+00:00
        identities:
        - identity_id: id_plen7kcp3t3rcyhzq6z3go64v4
          provider: siwe
          subject: eip155:1:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
          display: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
          created_at: 2026-09-26 08:00:00+00:00
          last_login_at: 2026-09-26 08:00:00+00:00
        - identity_id: id_xupnufatg6to3qmkjqvahsyhcu
          provider: github
          subject: '583231'
          display: octocat
          created_at: 2026-09-26 08:05:00+00:00
          last_login_at: null
        session:
          expires_at: 2026-10-03 08:00:00+00:00
          idle_timeout_secs: 86400
    linkOauthAlreadyOwn:
      summary: GitHub identity already linked to this user (idempotent)
      value:
        identity_id: id_xupnufatg6to3qmkjqvahsyhcu
        provider: github
        subject: '583231'
        display: octocat
        created_at: 2026-09-26 08:05:00+00:00
        last_login_at: null
    linkSiweCreated:
      summary: Link new wallet
      value:
        identity_id: id_5r4lvvqyinfzgphe5dn2hfd5fq
        provider: siwe
        subject: eip155:1:0x70997970c51812dc3a010c7d01b50e0d17dc79c8
        display: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'
        created_at: 2026-09-26 08:10:30+00:00
        last_login_at: null
    linkSiweWrongPurpose:
      summary: Link attempted with purpose=login challenge
      value:
        error:
          code: siwe_invalid
          message: invalid SIWE message or signature
          reason: signature
    errInternal:
      summary: Internal error
      value:
        error:
          code: internal
          message: internal error
    errUnauthenticatedLinkChallenge:
      summary: purpose=link without session
      value:
        error:
          code: unauthenticated
          message: missing or invalid session token
    reqSiweVerifyUnknownField:
      summary: Contains undefined fields
      value:
        message: 'console.blockvectra.com wants you to sign in with your Ethereum account:

          0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266


          Sign in to the BlockVectra RPC console.


          URI: https://console.blockvectra.com

          Version: 1

          Chain ID: 1

          Nonce: 3f7c0a9e5b1d4c2a8e6f0b3d9a7c5e1f

          Issued At: 2026-09-26T08:00:00Z

          Expiration Time: 2026-09-26T08:05:00Z'
        signature: '0x5f2a8c3e1d9b7f604a2e8c1b3d5f7a9e0c2b4d6f8a1c3e5b7d9f0a2c4e6b8d1f3a5c7e9b0d2f4a6c8e1b3d5f7a9c0e2b4d6f8a1c3e5b7d9f0a2c4e6b8d1f3a5c1c'
        chain_id: 1
    linkOauthCreated:
      summary: Link new GitHub identity
      value:
        identity_id: id_xupnufatg6to3qmkjqvahsyhcu
        provider: github
        subject: '583231'
        display: octocat
        created_at: 2026-09-26 08:05:00+00:00
        last_login_at: null
    reqUsernameTooLong:
      summary: 65 characters
      value:
        username: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    reqOauthToken:
      summary: Exchange login code and verifier
      value:
        code: rgc_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
        code_verifier: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
    payloadTooLarge:
      summary: Payload too large
      value:
        error:
          code: payload_too_large
          message: payload too large
    linkSiweAlreadyOwn:
      summary: Wallet already linked to this user (idempotent)
      value:
        identity_id: id_plen7kcp3t3rcyhzq6z3go64v4
        provider: siwe
        subject: eip155:1:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
        display: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
        created_at: 2026-09-26 08:00:00+00:00
        last_login_at: 2026-09-26 08:00:00+00:00
    unlinkLastIdentity:
      summary: Cannot unlink sole identity
      value:
        error:
          code: last_identity
          message: the last identity cannot be removed
    reqSiweVerify:
      summary: Submit raw message and signature
      value:
        message: 'console.blockvectra.com wants you to sign in with your Ethereum account:

          0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266


          Sign in to the BlockVectra RPC console.


          URI: https://console.blockvectra.com

          Version: 1

          Chain ID: 1

          Nonce: 3f7c0a9e5b1d4c2a8e6f0b3d9a7c5e1f

          Issued At: 2026-09-26T08:00:00Z

          Expiration Time: 2026-09-26T08:05:00Z'
        signature: '0x5f2a8c3e1d9b7f604a2e8c1b3d5f7a9e0c2b4d6f8a1c3e5b7d9f0a2c4e6b8d1f3a5c7e9b0d2f4a6c8e1b3d5f7a9c0e2b4d6f8a1c3e5b7d9f0a2c4e6b8d1f3a5c1c'
    errInvalidUsername:
      summary: Username exceeds length limit
      value:
        error:
          code: invalid_request
          message: username must be at most 64 characters and contain no control characters
          reason: invalid_username
    meUsernameUpdated:
      summary: After updating username
      value:
        username: alice
        created_at: 2026-09-26 08:00:00+00:00
        identities:
        - identity_id: id_plen7kcp3t3rcyhzq6z3go64v4
          provider: siwe
          subject: eip155:1:0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
          display: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
          created_at: 2026-09-26 08:00:00+00:00
          last_login_at: 2026-09-26 08:00:00+00:00
        - identity_id: id_xupnufatg6to3qmkjqvahsyhcu
          provider: github
          subject: '583231'
          display: octocat
          created_at: 2026-09-26 08:05:00+00:00
          last_login_at: null
        session:
          expires_at: 2026-10-03 08:00:00+00:00
          idle_timeout_secs: 86400
    linkSiweInUse:
      summary: Wallet already linked to another user
      value:
        error:
          code: identity_in_use
          message: this identity is linked to another user
    reqUsernameClear:
      summary: Clear username
      value:
        username: null
    reqUsernameSet:
      summary: Set username
      value:
        username: alice
    linkOauthInUse:
      summary: GitHub identity already linked to another user
      value:
        error:
          code: identity_in_use
          message: this identity is linked to another user
    errRateLimited:
      summary: Rate limit exceeded
      value:
        error:
          code: rate_limited
          message: rate limit exceeded
    linkOauthCodeReused:
      summary: Login code already used
      value:
        error:
          code: login_code_invalid
          message: login code is invalid, expired or already used
  parameters:
    IdentityIdPath:
      name: identity_id
      in: path
      required: true
      schema:
        type: string
        pattern: ^id_[a-z2-7]{26}$
      example: id_xupnufatg6to3qmkjqvahsyhcu
  responses:
    PayloadTooLarge:
      description: Request body exceeds 64 KiB limit. Exceeding this limit returns 413 (`payload_too_large`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            payloadTooLarge:
              $ref: '#/components/examples/payloadTooLarge'
    InternalError:
      description: Internal server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            errInternal:
              $ref: '#/components/examples/errInternal'
    TooManyRequests:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            errRateLimited:
              $ref: '#/components/examples/errRateLimited'
    Unauthorized:
      description: Missing or invalid session (without distinguishing cause).
      headers:
        WWW-Authenticate:
          $ref: '#/components/headers/WwwAuthenticate'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            errUnauthenticated:
              $ref: '#/components/examples/errUnauthenticated'
            errUnauthenticatedLinkChallenge:
              $ref: '#/components/examples/errUnauthenticatedLinkChallenge'
  securitySchemes:
    SessionToken:
      type: http
      scheme: bearer
      bearerFormat: rgs_<64-character lowercase hex>
      description: '`Authorization: Bearer rgs_...`, issued by `POST /auth/siwe/login` or `POST /auth/oauth/token`. Absolute TTL 7 days,

        idle 24 hours. Invalid returns 401 `unauthenticated` with `WWW-Authenticate: Bearer`.

        '