Skip to content

Latest commit

 

History

History
675 lines (487 loc) · 35.8 KB

File metadata and controls

675 lines (487 loc) · 35.8 KB

PoolTool 2026 — Frontend API Interface Specification

This document defines what the frontend client can expect from the PoolTool API: REST endpoints, WebSocket behavior, authentication, and data shapes. Base URL for REST is the API origin (e.g. https://api.pooltool.io). WebSocket URL is the same origin with path /ws (e.g. wss://api.pooltool.io/ws).


1. Authentication

1.1 Overview

  • Authentication is stake-key + password based. The login response includes a token and user_id. The client stores both (e.g. localStorage.pt_token, localStorage.pt_user_id) and sends the token on every request as Authorization: Bearer <token>.
  • Protected endpoints (all /api/user/* and PUT /api/admin_message/*) validate the token against the user_tokens table. User endpoints additionally verify that the token's user_id matches the path's user_id — a user can only access their own data. Admin endpoints require authority = 'administrator' in the users table.
  • Unauthenticate (logout): There is no server-side logout or token revocation. The client unauthenticates by discarding the stored token and user_id (e.g. clearing localStorage/sessionStorage). No API call is required.

1.2 Register

Endpoint: POST /auth/register

Request body (JSON):

Field Type Required Description
stake_key string Yes Cardano stake key (hex or bech32)
password string Yes User-chosen password

Responses:

  • 200: { "success": true, "user_id": "<uuid>" }
  • 409: Stake key already registered (body message)

1.3 Login

Endpoint: POST /auth/login

Request body (JSON):

Field Type Required Description
stake_key string Yes Registered stake key
password string Yes Password

Responses:

  • 200:
    { "success": true, "user_id": "<uuid>", "token": "<url-safe-token>" }
    The client should store user_id and token for subsequent requests and for identifying the logged-in user.
  • 401: Unknown address or invalid password (body message)

1.4 Verification (pool claiming / address verification)

Start verification: POST /auth/verify

Request body (JSON):

Field Type Required Description
stake_key string Yes Stake key to verify
user_id string Yes UUID of the user
password string Yes User password

Responses:

  • 200:
    • If already verified: { "status": "already_verified" }
    • If pending: { "status": "pending", "payment_address": "<addr>", "payment_amount": <lovelace> }
    • If new: same as pending; client must send exact payment_amount (in lovelace) to payment_address from the stake key to be verified.

Check verification status: GET /auth/verify/{stake_key}

Responses:

  • 200:
    • { "status": "no_pending_verification" }
    • { "status": "pending", "payment_amount": <number> }
    • { "status": "verified", "user_id": "<uuid>" }

1.5 Password Reset

Start reset: POST /auth/reset_password

Request body (JSON):

Field Type Required Description
stake_key string Yes Registered stake key
new_password string Yes Desired new password

Responses:

  • 200:
    { "status": "pending", "payment_address": "<addr>", "payment_amount": <lovelace> }
    Client must send exactly payment_amount to payment_address from the stake key. Once the on-chain payment is detected, the password is updated automatically.
  • 404: No account found for this stake key.

Check reset status: GET /auth/check_reset/{stake_key}

Responses:

  • 200:
    • { "status": "no_pending_reset" }
    • { "status": "pending", "payment_amount": <number> }
    • { "status": "completed" }

1.6 Query address (delegation info)

Endpoint: POST /auth/queryaddress

Request body (JSON): { "stake_key": "<stake_key>" }

Responses:

  • 200:
    • Not found: { "success": false, "message": "Address not found" }
    • Found:
      { "success": true, "epoch": <number>, "amount": <number>, "delegatedTo": "<pool_id>", "delegatedToTicker": "<ticker>" }

2. REST API Endpoints

All REST routes below are under the same origin. Prefix /api is used for most resources; /auth is used for auth (see §1). All responses are JSON unless noted.

2.1 Pools

Method Path Description
GET /api/pools List all non-retired pools (same shape as WebSocket pools snapshot). Each pool uses descriptive keys (see §4 Pool shape): pool_id, ticker, pool_name, live_stake, two_month_ros, one_month_ros, lifetime_ros, etc.
GET /api/pool/{pool_id} Returns { "pool": <wire-only>, "pool_stats": {...} }. pool uses descriptive keys only (e.g. pool_id, ticker, pool_name, live_stake, two_month_ros). pool_stats includes reward_account, pool_owners, reward_address, owners, live_stake, and other detail fields. pool_id in path can be bech32 (pool1...) or hex.
GET /api/pool/{pool_id}/history Pool history (blocks, assigned_slots, delegators_rewards, pool_fees, ros, stake). Query: limit (default 30).
GET /api/pool/{pool_id}/blocks/{epoch} Blocks produced by pool in the given epoch.
GET /api/pool/{pool_id}/awards Milestone awards (e.g. {"1": true, "10": true}).
GET /api/tickers2026 Pool tickers lookup. Returns a JSON object mapping pool IDs to ticker/name/group data. Served from DB (populated by periodic tickers task and uploaded to S3).

2.2 Blocks

Method Path Description
GET /api/blocks/{epoch} Blocks for epoch in competitive-block format: { blockHeight: { poolId: { hash: { ...blockData } } } }. Optional query params: limit, block_height.
GET /api/recent_block Latest block (single object).

2.3 Epoch

Method Path Description
GET /api/epoch/{epoch} Epoch data (epoch_blocks, last_block_time, etc.).
GET /api/epoch_params/{epoch} Epoch protocol parameters: monetary_expansion_rate, treasury_growth_rate, decentralisation, influence, max_bh_size, max_block_size, etc.
GET /api/livedata Current-epoch params (livedata): same shape as epoch_params for latest epoch — monetary_expansion_rate, treasury_growth_rate, decentralisation, influence, etc.
GET /api/active_stake/{epoch} Total active stake for epoch: { "epoch": <n>, "total_active_stake": <n> }.
GET /api/epoch_exchange_rates Exchange rates by epoch: { "<epoch>": <value>, ... }.

2.4 Ecosystem

Method Path Description
GET /api/ecosystem Ecosystem summary: total_staked, active_pools, delegators, rewardpot, reserves, epochLength (432000), activeSlotCoeff (0.05), and optionally maxLiveStake if present in DB.
GET /api/syncdata Sync status: keyed { "<key>": { "block": <n>, "bool": <bool> }, ... } plus numeric syncd, samples, majoritymax (always numbers for reporter ratio and live max tip).
GET /api/heights Per-pool tip heights from live_sync_stats. Returns a JSON object keyed by pool identifier with height values. Also available as { "latest_block": <number> } when only a single value is present.

2.5 Rewards / stake

Method Path Description
GET /api/stake_hist/{address} Stake history for a stake key (address). Loads from S3 archive and optionally builds recent epochs from DB. Returns a list of epoch records.
POST /api/pivotrewards Trigger pivot calculation. Body: { "stake_keys": ["<key1>", ...] }. Max 5 keys. Returns { "status": "processing", "keys": [...], "message": "Results will be pushed via WebSocket..." }. Subscribe to WebSocket channel stake_hist (with param address = stake key) before calling; results are pushed as a snapshot when ready (usually within seconds).

2.6 Users (authenticated context)

Endpoints use user_id in the path. The client must send Authorization: Bearer <token> (the token from login). The server validates the token and verifies the token's user matches the path user — users can only access their own data.

Method Path Description
GET /api/user/{user_id} Full user profile: user_id, created_at, authority, myAddresses, myPools, myApiKeys, settings, favorites, userType.
PUT /api/user/{user_id}/settings Update settings/favorites. Body: { "settings": {...}, "favorites": ["pool_id", ...] } (both optional).
POST /api/user/{user_id}/favorites Add favorite. Body: { "pool_id": "<pool_id>" }.
DELETE /api/user/{user_id}/favorites/{pool_id} Remove favorite.

2.7 Translations / i18n

Method Path Description
GET /api/translations List of available locale codes.
GET /api/translations/{locale} Translation data for locale (JSON object).
GET /api/languages Languages metadata object keyed by locale.

2.8 Admin

Method Path Description
GET /api/admin_message Current admin banner for web (no auth). Returns { "chillin": <bool>, "title": "<string>", "message": "<string>" }. chillin is true when no message is set.
GET /api/admin_message/{target} Admin banner for a specific target (e.g. web).
PUT /api/admin_message/{target} Set/clear admin banner. Requires admin token (Authorization: Bearer <token> from a user with authority = "administrator"). Body: { "title": "<string>", "message": "<string>" }. Send empty strings to clear.

2.9 FCM Push Notifications (Mobile)

These endpoints manage Firebase Cloud Messaging (FCM) tokens and notification subscriptions for the iOS and Android apps. No authentication is required — the FCM token itself acts as the device identity.

All endpoints are under /api/fcm. The pool_id field accepts either hex (abc123...) or bech32 (pool1...) format; the server converts bech32 to hex internally.

2.9.1 Register Token

Endpoint: POST /api/fcm/register

Registers or re-registers a device's FCM token. If the token already exists (e.g. after app reinstall or token refresh), it updates the platform and resets the invalid flag to false.

Request body (JSON):

Field Type Required Default Description
token string Yes — FCM device token
platform string No "unknown" "android", "ios", or "unknown"

Responses:

  • 200: { "status": "ok" }
  • 422: token is required (empty/whitespace-only token)

Notes:

  • If a token was previously marked invalid (due to failed sends), re-registering via this endpoint resets it to valid.
  • Platform values other than android, ios, or unknown are silently normalized to unknown.

2.9.2 Subscribe to Pool Alerts

Endpoint: POST /api/fcm/subscribe

Subscribe a device to push notifications for a specific pool and alert type. If the token is not yet registered, it is auto-created. If the subscription already exists, its config is updated.

Request body (JSON):

Field Type Required Description
token string Yes FCM device token
pool_id string Yes Pool ID (hex or bech32)
alert_type string Yes One of: block_production, fee, saturation, pledge
config object No Optional JSON config for this subscription (default {})

Responses:

  • 200: { "status": "ok" }
  • 400: Token was invalidated; re-register with POST /api/fcm/register — the token was marked invalid by a failed send. The client must call /api/fcm/register first to re-validate it.
  • 422: Validation error (empty token, invalid alert_type)

Alert types and their triggers:

alert_type When notification is sent Currently active
block_production Pool mints a new block (within 120s of block time — "live" blocks only) Yes
fee Pool changes its margin (fee), fixed cost, or pledge via a pool update cert Yes
saturation Pool's live stake exceeds the saturation threshold, checked at each epoch boundary Yes
pledge Reserved for pledge violation alerts (subscriptions accepted, notifications not yet wired) No (stored only)

2.9.3 Update Subscription Config

Endpoint: PUT /api/fcm/subscribe

Updates the config JSON for an existing subscription. Returns 404 if no matching subscription exists.

Request body (JSON):

Field Type Required Description
token string Yes FCM device token
pool_id string Yes Pool ID (hex or bech32)
alert_type string Yes One of: block_production, fee, saturation, pledge
config object Yes New config JSON to set

Responses:

  • 200: { "status": "ok" }
  • 404: Subscription not found
  • 422: Invalid alert_type

2.9.4 Unsubscribe from Pool Alerts

Endpoint: DELETE /api/fcm/subscribe

Removes a subscription. If alert_type is provided, removes only that specific subscription. If alert_type is omitted, removes all subscriptions for the token + pool combination.

Request body (JSON):

Field Type Required Description
token string Yes FCM device token
pool_id string Yes Pool ID (hex or bech32)
alert_type string No Specific alert type to remove, or omit for all

Responses:

  • 200: { "status": "ok" } (always 200, even if nothing was deleted)
  • 422: Invalid alert_type (if provided and not in allowed set)

2.9.5 Unsubscribe from All Pool Alerts

Endpoint: DELETE /api/fcm/subscribe/pool

Convenience endpoint: removes all subscriptions for a token + pool pair. Equivalent to calling DELETE /api/fcm/subscribe without alert_type.

Request body (JSON):

Field Type Required Description
token string Yes FCM device token
pool_id string Yes Pool ID (hex or bech32)

Responses:

  • 200: { "status": "ok" }

2.9.6 List Subscriptions

Endpoint: GET /api/fcm/subscriptions

Returns all active subscriptions for a device token.

Query parameters:

Param Type Required Description
token string Yes FCM device token
pool_id string No Filter to a specific pool (hex or bech32)

Responses:

  • 200: Array of subscription objects:
    [
      {
        "pool_id": "abc123...",
        "alert_type": "block_production",
        "config": {}
      },
      {
        "pool_id": "abc123...",
        "alert_type": "fee",
        "config": {}
      }
    ]
    Returns an empty array [] if no subscriptions exist.
  • 422: token is required

2.9.7 Unregister Token

Endpoint: POST /api/fcm/unregister

Permanently removes a device token and all of its subscriptions (cascade delete). Use when the user logs out of the app or disables notifications entirely.

Request body (JSON):

Field Type Required Description
token string Yes FCM device token

Responses:

  • 200: { "status": "ok" } (always 200, even if token didn't exist)
  • 422: token is required

2.9.8 Token Lifecycle and Invalidation

The server automatically manages token validity:

  1. On every send: When the server sends a push notification, Firebase reports which tokens are permanently invalid (unregistered device, sender mismatch). These tokens are marked invalid = true in the database.
  2. Invalid tokens are excluded: Subsequent notification sends skip tokens marked invalid. No unnecessary FCM traffic is generated.
  3. Re-registration resets validity: If a device gets a new FCM token (token refresh) or reinstalls the app, calling POST /api/fcm/register with the new (or same) token resets invalid to false.
  4. Subscribing with an invalid token is rejected: POST /api/fcm/subscribe returns HTTP 400 if the token is currently marked invalid. The client should call /api/fcm/register first.

Recommended app flow:

  1. On app startup, call POST /api/fcm/register with the current FCM token and platform.
  2. Call GET /api/fcm/subscriptions?token=... to load the user's current subscriptions.
  3. When the user adds/removes a pool from their watch list, call POST /api/fcm/subscribe or DELETE /api/fcm/subscribe accordingly.
  4. When the FCM SDK fires a token-refresh callback, call POST /api/fcm/register with the new token. Note: subscriptions are tied to the old token — the app should re-subscribe with the new token and unregister the old one.
  5. On logout or "disable notifications," call POST /api/fcm/unregister.

2.9.9 Push Notification Payload Format

Push notifications sent by the server have the following structure:

Android (via AndroidConfig):

  • notification.click_action: "FLUTTER_NOTIFICATION_CLICK"
  • notification.title / notification.body: Alert text

iOS (via APNSConfig):

  • aps.alert.title / aps.alert.body: Alert text
  • aps.sound: "default"

Data payload (all platforms):

  • click_action: "FLUTTER_NOTIFICATION_CLICK"
  • poolId: The hex pool ID that triggered the notification
  • Additional keys depending on the notification type may be added in the future.

2.10 Health

Method Path Description
GET /health { "status": "ok", "latest_block": <n>, "ws_clients": <n>, "ws_subscriptions": <n>, "periodic_tasks": {...} }.

2.11 Internal / not for frontend

  • POST /ouraconsume — Oura pipeline webhook; not for browser clients. Receives parsed chain events (Block, Transaction, TxInput, TxOutput, BlockEnd, StakeDelegation, StakeDeregistration, PoolRegistration, PoolRetirement, MoveInstantaneousRewardsCert).

3. WebSocket

3.1 Endpoint and connection

  • URL: Same origin as REST, path /ws (e.g. wss://api.pooltool.io/ws).
  • Protocol: Standard WebSocket. Messages are UTF-8 JSON text.
  • Connection: No query parameters or headers are required for basic connect. The server accepts the connection and does not require auth for the WebSocket itself; user-specific data is requested via the user_data channel with params.user_id.

3.2 Client → server (actions)

Every client message must be a JSON object with an action field. Optional fields: channel, params.

Action When to use channel params
subscribe Subscribe to a channel (and optionally get initial snapshot). Required. Channel name (see §3.4). Optional. Object used to scope the channel (e.g. pool_id, epoch, address, user_id).
unsubscribe Stop receiving a channel. Required. Same as used in subscribe. Optional. Must match the params used when subscribing.
pong Response to server ping (keepalive). — —

Examples:

{ "action": "subscribe", "channel": "pools" }
{ "action": "subscribe", "channel": "pool_stats", "params": { "pool_id": "abc123" } }
{ "action": "subscribe", "channel": "user_data", "params": { "user_id": "uuid-here" } }
{ "action": "unsubscribe", "channel": "pool_stats", "params": { "pool_id": "abc123" } }
{ "action": "pong" }

Invalid JSON or unknown action results in a server message { "type": "error", "msg": "..." }.

3.3 Server → client (message types)

All server messages are JSON objects with at least:

  • type — One of: ping, snapshot, update, error.

Ping (keepalive):
{ "type": "ping", "t": <unix_ts> }
Client should reply with { "action": "pong" }. If the client does not respond in time (see §3.5), the server closes the connection.

Snapshot (initial / full state):
{ "type": "snapshot", "channel": "<channel>", "params": <params>, "seq": <number>, "data": <payload> }
Sent once per subscription when the client subscribes (if the channel has a fetcher). This is the raw data after connect or reconnect: the client subscribes to the channels it needs, and the server immediately sends a snapshot for each. No separate "download raw data" call is needed — subscribing is the way to get initial state.

Update (incremental / live):
{ "type": "update", "channel": "<channel>", "params": <params>, "seq": <number>, "data": <payload> }
Pushed when backend updates that channel (e.g. new block, pool metadata, user settings). Payload shape is channel-specific; it may be a full replacement or a delta depending on the channel.

Error:
{ "type": "error", "msg": "<string>" }

3.4 Channels and subscription keys

Subscription key = channel + optional params (canonical form: channel:param1_value:param2_value). Params are used to scope the channel (e.g. which pool, which epoch). Below, "Params" are the keys the client can send in params when subscribing; the server uses them to fetch the snapshot and to match broadcasts.

Channel Params Snapshot on subscribe Live updates Typical snapshot payload
pools — Yes Yes (full list or per-pool updates) List of pool objects with descriptive keys (see §4): pool_id, ticker, pool_name, live_stake, two_month_ros, one_month_ros, lifetime_ros, etc.
pool_stats pool_id Yes Yes Single pool object (descriptive keys) + extra fields: description, metadata, relay_details, reward_account, pool_owners, live_stake, two_month_ros, one_month_ros, lifetime_ros, etc.
pool_history pool_id, optional limit Yes No { blocks, assigned_slots, delegators_rewards, pool_fees, ros, stake } by epoch.
recent_block — Yes Yes (pushed by Oura consume_block_end) Single latest block object.
epoch_data optional epoch Yes Yes (pushed on new block via block_competitive pg_notify and periodic ecosystem broadcast) Epoch summary (epoch_blocks, last_block_time, etc.).
epoch_params optional epoch Yes No Epoch protocol parameters.
ecosystem — Yes Yes (broadcast every 60s and on epoch transitions) Ecosystem summary object.
active_stake optional epoch Yes No { epoch, total_active_stake }.
blocks optional epoch, limit, block_height Yes Yes (pushed via block_competitive pg_notify) Competitive-block structure for that epoch.
pool_blocks pool_id, optional epoch Yes No List of block records for pool in epoch.
stake_hist address (stake key) Yes Yes (after pivot or updates) List of epoch records or pivot result.
epoch_exchange_rates — Yes No { "<epoch>": <value>, ... }.
syncdata — Yes Yes (pushed via syncdata_updated pg_notify from tipsApiApp) Keyed { "<key>": { "block", "bool" }, ... } plus numeric syncd, samples, majoritymax.
heights — Yes Yes (pushed via heights_updated pg_notify from tipsApiApp) Per-pool tip heights object.
awards pool_id Yes No { "1": true, "10": true, ... }.
user_data user_id Yes Yes (e.g. after settings update) User profile (myAddresses, myPools, settings, favorites, userType, etc.).
admin_message — Yes Yes (after admin sets/clears message) { "chillin": <bool>, "title": "<string>", "message": "<string>" }.

3.5 Heartbeat and disconnect

  • Heartbeat: Server sends ping at a configurable interval (default 30s). Client must respond with pong. If the server does not receive a pong within the heartbeat timeout (default 10s after the ping interval), it closes the connection.
  • Disconnect: Client disconnects by closing the WebSocket (e.g. ws.close()). No "goodbye" message is required. On disconnect, the server clears all subscriptions for that connection.
  • Reconnect: After a reconnect, the client must send subscribe again for each channel it needs; the server will send a fresh snapshot for each, providing the raw data for that channel. There is no server-side session or "resume subscriptions" — re-subscribe after every connect.

3.6 Raw data after connect/reconnect

  • Connect: Open WebSocket → send one or more subscribe messages → receive snapshot messages with full state for each channel.
  • Reconnect: Same: open a new WebSocket, then send the same subscribe messages as before; the server sends snapshots again. The client does not call a separate "download raw data" REST endpoint for WebSocket state — the snapshots are the raw data for the subscribed channels.

For REST-only raw data (e.g. full pool list, full epoch blocks), use the REST endpoints in §2; they return the same data as the corresponding WebSocket snapshots.


4. Data shapes (summary)

4.1 Pool (descriptive keys)

Pool list (GET /api/pools, WS pools) and pool object (GET /api/pool/{id} → pool, WS pool_stats) use descriptive key names only. No compressed/short keys.

Key Type Description
pool_id string Pool identifier (hex).
ticker string Pool ticker symbol.
pool_name string Display name.
group_name string Group name (optional).
online_relays number Count of online relays.
offline_relays number Count of offline relays.
cost number Fixed cost (lovelace).
margin number Margin (percentage, 0–100).
pledge number Pledge (lovelace).
pool_pledge_value number Pool pledge value.
future_pledge number | null Pending pledge.
future_pledge_epoch number | null Epoch for future pledge.
future_cost number | null Pending cost.
future_cost_epoch number | null Epoch for future cost.
future_margin number Pending margin (%).
future_margin_epoch number | null Epoch for future margin.
itn_verified boolean ITN verified flag.
epoch_blocks number Blocks in current epoch.
epoch_blocks_epoch number | null Epoch of epoch_blocks.
life_blocks number Lifetime blocks.
retired boolean Pool retired.
genesis boolean Genesis pool.
rank number Rank.
assigned_slots number Assigned slots (current).
assigned_slots_epoch number | null Epoch of assigned_slots.
imposter any | null Imposter flag.
lifetime_per_blocks number Lifetime blocks (per).
lifetime_per_slots number Lifetime slots (per).
delegator_count number Number of delegators.
future_retired boolean Pending retirement.
future_retired_epoch number | null Epoch for future retirement.
live_stake number Active stake (lovelace).
protocol_major number Protocol major version.
protocol_minor number Protocol minor version.
two_month_ros number | null ROS over last ~2 months (12 epochs).
one_month_ros number | null ROS over last ~1 month (6 epochs).
lifetime_ros number | null Lifetime ROS.

Old (compressed) → New (descriptive) key names

Previously the API used short keys to save bandwidth. These are no longer sent; the table below is for migration reference only.

Old key New key
id pool_id
t ticker
n pool_name
g group_name
o online_relays
oo offline_relays
f cost
m margin
p pledge
ap pool_pledge_value
fp future_pledge
fpe future_pledge_epoch
ff future_cost
ffe future_cost_epoch
fm future_margin
fme future_margin_epoch
i itn_verified
b epoch_blocks
eb epoch_blocks_epoch
l life_blocks
d retired
x genesis
r rank
z assigned_slots
ez assigned_slots_epoch
xx imposter
zl lifetime_per_blocks
zs lifetime_per_slots
dc delegator_count
fd future_retired
fde future_retired_epoch
bs (removed; use live_stake only)
live_stake live_stake
pm protocol_major
pn protocol_minor
tr two_month_ros
sr one_month_ros
lros lifetime_ros

4.2 Other data shapes

  • Pool stats (detail): Returned under pool_stats on GET /api/pool/{pool_id}. Includes reward_account, pool_owners, reward_address, owners, live_stake, description, homePage, metadataHash, metadataUrl, relay_details, extended_json, firstEpoch, public_note, pooltoolbot_subscribers. The same pool's wire object (descriptive keys) is returned as pool.
  • Block (recent / list): block, slot, epoch, epoch_slot, hash, pool_id, pool_ticker, timestamp, transactions, fees, output, body_size, and in competitive view: leaderPoolId, leaderPoolTicker, time, chained, competitive, slotbattle, etc.
  • Epoch data: epoch, epoch_blocks, epoch_feess, last_block_time, epoch_tx_count, epoch_output, expected_blocks.
  • Epoch params / livedata: epoch, influence, max_bh_size, max_block_size, max_epoch, monetary_expansion_rate, optimal_pool_count, protocol_major, protocol_minor, treasury_growth_rate, decentralisation, treasury, reserves, reward_pot, epoch_feess. Use GET /api/epoch_params/{epoch} or GET /api/livedata (current epoch) for monetary_expansion_rate, treasury_growth_rate, decentralisation, influence.
  • Ecosystem: current_epoch, total_utxo, saturation, saturated, optimal_pools, treasury, reserves, rewardpot, total_staked, active_pools, delegators, epochLength (number), activeSlotCoeff (number), and optionally maxLiveStake when present.
  • Syncdata: Keyed entries { "<key>": { "block": <n>, "bool": <bool> }, ... } plus numeric syncd (count of reporters in sync), samples (total reporter count), majoritymax (max reported block height; use for "live max tip" or fall back to recent_block.block).
  • User: user_id, created_at, authority, myAddresses, myPools, myApiKeys, settings, favorites, userType.
  • Stake history (per epoch): epoch, amount, operator_rewards, stake_rewards, delegated_to_pool, delegated_to_ticker, forecast; pivot result adds lifeAmount, lifeOperatorRewards, lifeStakeRewards, operatorRewards, stakeRewards, rewardsSentTo, rewardAddrDetails, epochsStaked.

Exact field semantics and types can be inferred from the backend fetchers and REST handlers; this spec gives the frontend a single place for expected endpoints, flows, and payload roles.


5. Live data flow

This section documents how live data reaches the frontend, including the tipsApiApp integration.

5.1 tipsApiApp (port 3003) → PoolTool API (port 3004)

tipsApiApp is a separate FastAPI process (the original tips/slots processor) that handles pool operator tip reports. It writes to Postgres and fires pg_notify events that the PoolTool API listens for:

pg_notify channel WS channel updated Source
syncdata_updated syncdata tipsApiApp writes sync stats to live_sync_stats
heights_updated heights tipsApiApp writes per-pool heights to live_sync_stats
block_competitive blocks, epoch_data tipsApiApp writes competitive block analysis

5.2 Oura chain follower → PoolTool API

Oura follows the chain at tip-6 and sends parsed events to POST /ouraconsume. The chain consumer updates:

Event WS channels updated FCM push notifications
Block + BlockEnd recent_block, blocks, pool_blocks, epoch_data block_production (live blocks only)
PoolRegistration pools, pool_stats fee (pledge/cost/margin changes)
PoolRetirement pools —
StakeDelegation (DB only) —
Epoch transition ecosystem, epoch_params, active_stake, pool_stats (all pools) saturation (saturated pools)

5.3 Periodic tasks → WS broadcasts

Task Interval WS channels affected
ecosystem_broadcast 60s ecosystem
process_orphans 20s (updates DB; blocks reflects changes on next query)
battle_data_s3 30s (S3 only)
update_metadata 1h pools (via DB)
update_relays 1h pools (via DB)
battle_trends 1h (S3 only)
check_pledge 24h (Telegram notifications)
update_tickers 6h (S3 + DB)
exchange_rates 1h (DB; epoch_exchange_rates reflects on next query)

6. Quick reference

Concern Behavior
Authenticate POST /auth/login with stake_key + password → store user_id and token. Send Authorization: Bearer <token> on all subsequent requests.
Unauthenticate Client discards token and user_id; no API call.
Reset password POST /auth/reset_password with stake_key + new_password → send ADA to returned address → poll GET /auth/check_reset/{stake_key} until completed.
Subscribe to events WebSocket to /ws → send { "action": "subscribe", "channel": "<name>", "params": {...} } → receive snapshot then update messages.
Raw data after connect/reconnect Send subscribe for each channel; server replies with snapshot containing full state. No separate download endpoint.
Disconnect Close WebSocket. Server cleans up subscriptions.
Reconnect Open new WebSocket, send same subscribe messages again to get new snapshots.
REST All read endpoints are GET unless noted; user mutations use PUT/POST/DELETE with user_id in path.
FCM push (mobile) On app start: POST /api/fcm/register with device token. Subscribe to pools: POST /api/fcm/subscribe. List subs: GET /api/fcm/subscriptions?token=.... On token refresh: register new token, re-subscribe, unregister old. On logout: POST /api/fcm/unregister.
CORS All origins allowed; credentials allowed.
API docs Interactive docs at /docs (FastAPI Swagger).