XChain Platform Hub — API Reference

All methods are called via HTTP POST with JSON-RPC 2.0 format:

curl -X POST http://localhost:10000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"ping","id":1}'

Config Management

ping

Health check.

Request:

{"jsonrpc":"2.0","method":"ping","id":1}

Response:

{"status":"success"}

health

Detailed health check. Unlike ping (which only confirms the HTTP server is up and the DB pool answers a probe query), health also reports the DB circuit-breaker state and — on oracle-running (P2P-enabled) hubs — oracle round freshness, so an operator can distinguish a healthy hub from one that is up but stalled on a tripped database connection or a stale price feed. Returns HTTP 503 (with the same body) when status is "degraded".

Request:

{"jsonrpc":"2.0","method":"health","id":1}

Response:

{
  "status":"healthy",
  "db":true,
  "dbCircuit":"closed",
  "oracle_last_finalized_age_s":120,
  "oracle_stale":false,
  "oracle_staleness_threshold_s":1200
}
Field Type Description
status string "healthy" when the DB answers, the circuit is not open, and the oracle is not stale; "degraded" otherwise (also sets HTTP 503).
db boolean Whether a SELECT 1 probe against the DB pool succeeded within 2s.
dbCircuit string|null DB circuit-breaker state ("closed", "open", "half-open"), or null if no DB handle is configured. A value of "open" forces status to "degraded".
oracle_last_finalized_age_s number|null Seconds since the most recently finalized oracle round (price_snapshots with status = 'finalized'). null on config-only hubs (no oracle), when the DB probe failed, or when no round has ever finalized (fresh node).
oracle_stale boolean true when oracle_last_finalized_age_s exceeds oracle_staleness_threshold_s. Forces status to "degraded". Always false on config-only hubs and fresh nodes.
oracle_staleness_threshold_s number|null Staleness threshold in seconds. Defaults to twice the ORACLE_ROUND_INTERVAL; override with the ORACLE_STALENESS_THRESHOLD_S environment variable. null on config-only hubs or when the DB probe failed.

Note: the three oracle_* fields are only populated on hubs running the oracle (P2P-enabled). A config-only hub mints no rounds and reports them as null/false.

getallconfigs

Returns all service configs wrapped in an envelope: { configs, seq, watermark }.

  • configs — the nested config tree: { coin: { network: { module: { param: value } } } }.
  • seq — the last committed consensus sequence number (0 on a fresh node with no committed config changes yet).
  • watermark — the high-water mark of the configs table as an epoch-seconds integer (the newest updated_at across all rows, or 0 when the table is empty). See Delta polling below.

Request:

{
  "jsonrpc":"2.0",
  "method":"getallconfigs",
  "params":{"since_updated_at":0},
  "id":1
}

since_updated_at is optional (defaults to 0). See Delta polling below.

Response:

{
  "configs": {
    "bitcoin": {
      "mainnet": {
        "xchain-decoder": {
          "host": "192.168.1.10",
          "port": "8332",
          "db_host": "mariadb",
          "db_port": "3306",
          "name": "XChain_BTC_Mainnet_Decoder",
          "user": "xchain_decoder",
          "pass": "password"
        },
        "xchain-indexer": { ... },
        "xchain-explorer": { ... }
      },
      "testnet": { ... }
    },
    "litecoin": { ... },
    "dogecoin": { ... }
  },
  "seq": 42,
  "watermark": 1717400000
}

Note: the config tree lives under result.configs, not at the top level. Read result.configs.bitcoin.mainnet..., not result.bitcoin.mainnet....

Delta polling: watermark is an epoch-seconds timestamp the consumer should retain and pass back as since_updated_at on its next getallconfigs call. When since_updated_at > 0, the hub returns only the config rows that changed strictly after that instant (a delta — typically empty on a quiet poll) rather than the full tree, along with the new watermark to carry into the following poll. Omitting since_updated_at (or passing 0) returns the complete config tree, so first fetches and consumers that don’t track the watermark are unaffected. The configs table is upsert-only (rows are never deleted), so merging successive deltas reconstructs exactly what a full fetch would have returned.

updateconfig

Upserts service configs from a nested JSON object. In validator mode, the write goes through PBFT consensus. In standalone mode, it writes directly to MariaDB.

Request:

{
  "jsonrpc":"2.0",
  "method":"updateconfig",
  "params":{
    "config":{
      "BTC":{
        "mainnet":{
          "xchain-decoder":{
            "host":"192.168.1.10",
            "port":"8332"
          }
        }
      }
    }
  },
  "id":1
}

Response:

{"status":"success"}

Config parameters stored per coin/network/module: host, port, service_port, db_host, db_port, name, user, pass.

Validator Management

registervalidator

Bootstrap-register a validator. The signing public key must be a 64-character hex string (Ed25519 public key).

Request:

{
  "jsonrpc":"2.0",
  "method":"registervalidator",
  "params":{
    "signing_pubkey":"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
    "addr":"validator1.example.com"
  },
  "id":1
}

Response:

{"status":"success"}

syncvalidators

Bulk sync the validator set from external staking data (e.g., from the indexer). Replaces the current set and reloads all subsystem validator sets.

Each validator object carries signing_pubkey, addr, status, and an optional comma-separated chains list (used for cross-chain quorum filtering; omit or leave empty to support all chains). Validator capabilities (price, cross_chain, oracle_publish, attestation) are not part of this object — a pubkey auto-qualifies for each capability whose governance-configured minimum stake its aggregate active stake meets. There is no tier field.

Request:

{
  "jsonrpc":"2.0",
  "method":"syncvalidators",
  "params":{
    "validators":[
      {"signing_pubkey":"a1b2c3...","addr":"validator1.example.com","status":"active","chains":"BTC,LTC"},
      {"signing_pubkey":"d4e5f6...","addr":"validator2.example.com","status":"active","chains":"BTC,LTC,DOGE"}
    ]
  },
  "id":1
}

Response:

{"status":"success","count":2}

getvalidators

List all active validators.

Request:

{"jsonrpc":"2.0","method":"getvalidators","id":1}

Response:

[
  {"signing_pubkey":"a1b2c3...","addr":"validator1.example.com","status":"active","chains":"BTC,LTC"},
  {"signing_pubkey":"d4e5f6...","addr":"validator2.example.com","status":"active","chains":"BTC,LTC,DOGE"}
]

getvalidatorstatus

Detailed status for a specific validator: info, unclaimed rewards, recent rewards, and slash proposals.

Request:

{
  "jsonrpc":"2.0",
  "method":"getvalidatorstatus",
  "params":{"signing_pubkey":"a1b2c3..."},
  "id":1
}

Response:

{
  "validator": {"signing_pubkey":"a1b2c3...","addr":"validator1.example.com","status":"active"},
  "unclaimed_rewards": "150.00000000",
  "recent_rewards": [...],
  "slash_proposals": [...]
}

Oracle / Price Data

getoraclesubmissions

Diagnostic method returning current round submissions per validator.

Request:

{"jsonrpc":"2.0","method":"getoraclesubmissions","id":1}

Response:

[
  {"round_number":42,"coin_pair":"BTC/USD","signing_pubkey":"a1b2c3...","price":"67500.00"},
  {"round_number":42,"coin_pair":"LTC/USD","signing_pubkey":"a1b2c3...","price":"85.50"}
]

getpricesnapshots

Returns recent finalized price snapshots.

Request:

{
  "jsonrpc":"2.0",
  "method":"getpricesnapshots",
  "params":{"limit":10},
  "id":1
}

Response:

[
  {"round_number":42,"coin_pair":"BTC/USD","price":"67500.00","status":"finalized","created_at":"2026-04-06T12:00:00.000Z"},
  {"round_number":42,"coin_pair":"LTC/USD","price":"85.50","status":"finalized","created_at":"2026-04-06T12:00:00.000Z"},
  {"round_number":42,"coin_pair":"DOGE/USD","price":"0.0825","status":"finalized","created_at":"2026-04-06T12:00:00.000Z"}
]

getprice

Returns the latest finalized price for a specific coin pair.

Request:

{
  "jsonrpc":"2.0",
  "method":"getprice",
  "params":{"coin_pair":"BTC/USD"},
  "id":1
}

Response:

{"coin_pair":"BTC/USD","price":"67500.00","round_number":42,"status":"finalized"}

pushchaintip (write — requires API key)

Pushes a chain tip update from an indexer. The hub uses this to anchor oracle rounds to the BTC chain tip (replacing the hardcoded reference_block=0 bug).

Request:

{
  "jsonrpc":"2.0",
  "method":"pushchaintip",
  "params":{"coin":"BTC","block_height":850010,"block_time":1712500000},
  "id":1
}

Response:

{"status":"success"}

Stored in the configs table as (coin, mainnet, chain_tips, block_height|block_time). Read by OracleRound._executeRound() at the start of each PBFT round.

pushpriceround (write — requires API key)

Pushes a validated PRICE v0 round from an indexer for cross-chain aggregation. The hub deduplicates by round_number (first valid submission wins) and writes to price_snapshots.

Request:

{
  "jsonrpc":"2.0",
  "method":"pushpriceround",
  "params":{
    "source_chain":"DOGE",
    "round":850010,
    "timestamp":1712500000,
    "pairs":[{"pair":"BTC/USD","price":"100000.12345678"},{"pair":"BTC/EUR","price":"92000.00000000"}],
    "sigs":[{"pubkey":"aabb...","sig":"ccdd..."}],
    "action_index":12345,
    "block_index":850010
  },
  "id":1
}

Response:

{"accepted":true}

Or {"accepted":false,"reason":"duplicate"} if the round already exists. The indexer must validate PBFT signatures locally before pushing — the hub trusts indexer validation.

pushoracleprice (write — requires API key)

Pushes a validated PRICE v1 user oracle price from an indexer. The hub applies the 24-hour lock window and writes to oracle_prices.

Request:

{
  "jsonrpc":"2.0",
  "method":"pushoracleprice",
  "params":{
    "source_chain":"DOGE",
    "source_address":"D1xx...",
    "coin":"BTC",
    "tick":"PEPECASH",
    "fiat":"JPY",
    "value":"7.50000000",
    "fee":"0.01",
    "memo":"hourly update",
    "block_time":1712500000,
    "action_index":12345
  },
  "id":1
}

Response:

{"accepted":true}

The hub looks up any prior price for (source_address, coin, tick, fiat). First broadcast: effective_at = block_time (immediate). Subsequent updates: effective_at = block_time + 86400 (24-hour delay).

Hub DB Sync (REST + WebSocket)

The hub exposes a separate channel for replicating cross-chain infrastructure tables (price_snapshots, oracle_prices) to indexers’ local hub DB copies. Used in geographically distributed deployments where indexers run on different hosts from the hub.

GET /hub-db/snapshot/price_snapshots

Returns rows from the price_snapshots table after since_id (paginated for incremental bootstrap).

Query parameters:

  • since_id (optional, default 0) — return rows where id > since_id
  • limit (optional, default 10000, max 10000)

Response:

{
  "table": "price_snapshots",
  "rows": [
    {"id":1,"round_number":850010,"coin_pair":"BTC/USD","price":"100000.12345678","reference_block":850010,"reference_chain":"BTC","block_timestamp":1712500000,"validator_count":5,"consensus_round":1,"consensus_proof":"[...]","status":"finalized","source_chain":"DOGE","source_action_index":12345,"created_at":"2026-04-06T12:00:00.000Z"}
  ],
  "count": 1
}

GET /hub-db/snapshot/oracle_prices

Returns rows from the oracle_prices table after since_id. Same format as above.

GET /hub-db/subscribe (WebSocket upgrade — requires Authorization: Bearer <HUB_API_KEY>)

WebSocket channel that pushes row:inserted events whenever the hub inserts new price_snapshots or oracle_prices rows. Each subscriber receives a JSON message per insertion:

Message format:

{"type":"row:inserted","table":"price_snapshots","row":{...}}

Indexers use the bootstrap REST snapshots followed by this WebSocket subscription to maintain a complete local copy of the hub’s cross-chain price tables. Backpressure handling drops connections that exceed WS_BACKPRESSURE_LIMIT buffered messages.

Fee Quotes

getfeequote

Calculates the native coin fee amount for a given action. Converts gas units → XCHAIN amount → native coin amount using the latest oracle price.

Request:

{
  "jsonrpc":"2.0",
  "method":"getfeequote",
  "params":{"action":"ISSUE","chain":"BTC"},
  "id":1
}

Response:

{
  "action":"ISSUE",
  "chain":"BTC",
  "gas_units":1000,
  "xchain_amount":"10.00000000",
  "native_amount":"0.00014815"
}

Supported action names: ISSUE, ISSUE_SUBTOKEN, VM_EXECUTE_BASE, VM_DEPLOY_BASE, AIRDROP_PER_RECIPIENT, and others matching the platform’s gas fee schedule.

Cross-Chain Attestations

requestattestation

Initiates a PBFT attestation consensus round for a cross-chain action.

Request:

{
  "jsonrpc":"2.0",
  "method":"requestattestation",
  "params":{
    "source_chain":"bitcoin",
    "source_action_index":12345,
    "dest_chain":"litecoin"
  },
  "id":1
}

Response:

{"status":"success","attestation_id":"bitcoin:12345:litecoin"}

getattestation

Get a specific attestation record by source chain and action index.

Request:

{
  "jsonrpc":"2.0",
  "method":"getattestation",
  "params":{
    "source_chain":"bitcoin",
    "source_action_index":12345
  },
  "id":1
}

Response:

{
  "attestation_id":"bitcoin:12345:litecoin",
  "source_chain":"bitcoin",
  "source_action_index":12345,
  "dest_chain":"litecoin",
  "status":"attested",
  "consensus_proof":{...}
}

getattestations

Query attestation records by status.

Request:

{
  "jsonrpc":"2.0",
  "method":"getattestations",
  "params":{"status":"attested","limit":50},
  "id":1
}

Response:

[
  {"attestation_id":"bitcoin:12345:litecoin","status":"attested",...},
  {"attestation_id":"bitcoin:12340:dogecoin","status":"attested",...}
]

Swap Tracking

initiateswap

Record a cross-chain swap initiation. The swap auto-progresses to “attested” when the corresponding attestation finalizes.

Request:

{
  "jsonrpc":"2.0",
  "method":"initiateswap",
  "params":{
    "source_chain":"bitcoin",
    "source_action_index":12345,
    "dest_chain":"litecoin",
    "dest_action_index":67890
  },
  "id":1
}

Response:

{"status":"success"}

getswap

Get a specific swap record.

Request:

{
  "jsonrpc":"2.0",
  "method":"getswap",
  "params":{
    "source_chain":"bitcoin",
    "source_action_index":12345
  },
  "id":1
}

Response:

{
  "source_chain":"bitcoin",
  "source_action_index":12345,
  "dest_chain":"litecoin",
  "dest_action_index":67890,
  "status":"attested"
}

getswaps

Query swap records by status.

Request:

{
  "jsonrpc":"2.0",
  "method":"getswaps",
  "params":{"status":"initiated","limit":50},
  "id":1
}

Swap statuses: initiated, attested, executed, settled, failed.

Reorg Handling

reportreorg

Report a detected blockchain reorg. Triggers PBFT consensus and hub state rollback if confirmed.

Request:

{
  "jsonrpc":"2.0",
  "method":"reportreorg",
  "params":{
    "chain":"bitcoin",
    "reorg_height":893000,
    "timestamp":1743690000
  },
  "id":1
}

Response:

{"status":"success"}

getreorghistory

Query confirmed reorg attestation history.

Request:

{
  "jsonrpc":"2.0",
  "method":"getreorghistory",
  "params":{"limit":20},
  "id":1
}

Response:

[
  {"chain":"bitcoin","reorg_height":893000,"timestamp":1743690000,"confirmed_at":"2026-04-06T12:00:00.000Z"}
]

Governance

propose

Submit a governance proposal for a parameter change. Must be an active validator.

Request:

{
  "jsonrpc":"2.0",
  "method":"propose",
  "params":{
    "parameter":"ORACLE_ROUND_INTERVAL",
    "current_value":"600000",
    "proposed_value":"300000",
    "rationale":"Faster oracle updates for improved price freshness"
  },
  "id":1
}

Response:

{"status":"success","proposal_id":1}

vote

Cast a vote on an active governance proposal. Must be an active validator.

Request:

{
  "jsonrpc":"2.0",
  "method":"vote",
  "params":{
    "proposal_id":1,
    "vote":"approve"
  },
  "id":1
}

Vote values: approve, reject.

Response:

{"status":"success"}

getproposals

List governance proposals, optionally filtered by status.

Request:

{
  "jsonrpc":"2.0",
  "method":"getproposals",
  "params":{"status":"voting"},
  "id":1
}

Response:

[
  {
    "proposal_id":1,
    "parameter":"ORACLE_ROUND_INTERVAL",
    "current_value":"600000",
    "proposed_value":"300000",
    "rationale":"Faster oracle updates for improved price freshness",
    "proposer":"a1b2c3...",
    "status":"voting",
    "created_at":"2026-04-06T12:00:00.000Z"
  }
]

Proposal statuses: voting, passed, failed.

getproposal

Get a specific proposal with all associated votes.

Request:

{
  "jsonrpc":"2.0",
  "method":"getproposal",
  "params":{"proposal_id":1},
  "id":1
}

Response:

{
  "proposal": {
    "proposal_id":1,
    "parameter":"ORACLE_ROUND_INTERVAL",
    "current_value":"600000",
    "proposed_value":"300000",
    "status":"voting"
  },
  "votes": [
    {"signing_pubkey":"a1b2c3...","vote":"approve"},
    {"signing_pubkey":"d4e5f6...","vote":"reject"}
  ]
}

Telemetry (REST)

Anonymous node-operator usage telemetry is served as plain REST routes (not JSON-RPC) on the same hub HTTP port. See TELEMETRY_API.md for full request bodies, query parameters, response shapes, and field-by-field reference. Operator-facing data policy lives in operations/TELEMETRY.md.

Method Path Auth Purpose
POST /telemetry none Ingest one anonymous usage ping (fire-and-forget).
GET /telemetry/summary none Aggregate-only census (distribution counts; ?days=1..365, default 30).
GET /telemetry/operators x-api-key: <TELEMETRY_ADMIN_KEY> Per-install operator detail (?days=1..365, default 30).

Copyright © 2025–2026 Dankest, LLC

Based on XChain Platform by Dankest, LLC – https://dankest.llc

Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later) with a commercial license available for proprietary use.

You may use, modify, and distribute this material under the terms of the License. See LICENSE and NOTICE for full terms. See the licensing overview.

Edit this page on GitHub ↗