Seneschal Data API

Pay-per-call data over HTTP and the Model Context Protocol. The headline product is Private Watch — view-key payment webhooks for Monero & Zcash (below). Also free and public: DeFi-liquidation telemetry (Aave, Morpho, Spark, Compound) and Ethereum block-builder market share from our own shadow recorder. No signup, no API key.

No signup. No API key. No rate limit beyond DoS protection (120 req/min/IP). Submit bugs and feature requests to @OrknetP on Telegram. — or open the live stats dashboard to see the data without writing a single line.
New · 2026-06-05: Zcash patched a critical Orchard bug — a supply bug, not a privacy one. What happened, what node operators must do, and why view-key payment monitoring is unaffected »

Private Watch — the headline product

Get an HMAC-signed webhook the instant a Monero or Zcash payment lands at your address. You supply a view key (read-only, cannot spend), an address, and a webhook URL. We POST you a notification on every balance change. Prepaid credit meter: $0.02 per day idle + $0.005 per webhook delivered — no account, no API key, no node.

Who it’s for: merchants accepting XMR or ZEC, donation pages, tip jars, payment bots, anyone selling something off-platform who would rather not run a 100 GB monerod or 300 GB zebra just to know whether the customer paid. View keys never let us move funds; if our DB leaks we can’t spend anything.

How the meter works: the first $0.10 (paywall on watch creation) buys 5 idle days. After that, top up any amount via dedicated paywalled paths: $0.10, $1.00, or $5.00 a pop. Every webhook body carries a credit block so your receiver always knows how much it has left. We’ll fire one low_credit warning when you have ~2 idle days remaining, even if no payments have come in.

Pay in privacy coin, not just USDC. A Monero/Zcash service that only took stablecoins on a transparent chain would be missing the point. So you can top a watch up by sending XMR or ZEC directly: POST /v1/private/topup-crypto is free to call and returns a receiving address plus the exact amount to send (Monero uses a unique-amount invoice tag; Zcash uses a memo) at a USD rate locked for the quote window. We detect your payment with the same view-key scanner this product sells — dogfooding the whole way down — and we hold only a view key for that wallet, never a spend key. No wallet connection, no x402, no API key.

Why it’s cheap: small users pay cents per month even with no traffic. Heavy users pay a few dollars even after thousands of webhook deliveries. Compare to a self-hosted full node (~$30/mo VPS + storage), light-wallet subscriptions ($5–$50/mo), or merchant gateways (1–2%): the only ones with skin in the game.

# 1. Create a watch (one x402 payment of $0.10 seeds $0.10 credit)
curl -s https://api.seneschal.space/v1/private/watch \
  -X POST -H 'content-type: application/json' \
  -H 'x-payment: <eip3009 payload, $0.10 USDC on Base>' \
  -d '{
    "chain": "monero",
    "address": "44AFFq…",
    "viewKey": "0123…",
    "webhookUrl": "https://shop.example.com/webhook/xmr"
  }'

# 2. Top up later (a $1 top-up buys ~50 idle days):
curl -X POST https://api.seneschal.space/v1/private/topup-1 \
  -H 'x-payment: <eip3009 payload, $1.00 USDC on Base>' \
  -H 'content-type: application/json' \
  -d '{"watchId":"...","watchToken":"..."}'

# 2b. ...or pay in Monero/Zcash instead (FREE quote, no x402, no key):
curl -X POST https://api.seneschal.space/v1/private/topup-crypto \
  -H 'content-type: application/json' \
  -d '{"watchId":"...","watchToken":"...","chain":"monero","amountUsdCents":500}'
# -> { quoteId, payTo, amount:{display,coin}, memo, confirmations, expiresAt }
# Send the exact amount (Monero) or include the memo (Zcash), then poll:
curl -s -H 'x-watch-token: ...' \
  https://api.seneschal.space/v1/private/topup-crypto/<quoteId>

# 3. One-off historical lookup (returns notes; view key never persists):
curl -X POST https://api.seneschal.space/v1/private/historical \
  -H 'x-payment: <eip3009 payload, $0.50 USDC on Base>' \
  -H 'content-type: application/json' \
  -d '{"chain":"zcash","address":"u1...","viewKey":"uview1...","includeNotes":true}'

# 4. FREE: derive a Zcash UFVK from a BIP-39 mnemonic (loud warning, rate-limited):
curl -X POST https://api.seneschal.space/v1/private/derive-viewkey \
  -H 'content-type: application/json' \
  -d '{"chain":"zcash","phrase":"abandon abandon ... 24 words"}'

Open WalletConnect panel » Full integration guide Free derive form /v1/private/info Live counters

Four endpoints, one prepaid balance

Create a watch

$0.10 · x402 · seeds $0.10 credit

POST /v1/private/watch — HMAC-signed webhook on every balance change. View key encrypted at rest, polled every 3 min.

Top up credit

USDC (x402) or XMR/ZEC · any amount

POST /v1/private/topup{,-1,-5,-custom} — $1 buys ~50 idle days. Or pay in coin (free quote) via POST /v1/private/topup-crypto. low_credit webhook tells you when to top up.

Historical lookup

$0.50 · one-off · view key in-memory only

POST /v1/private/historical — returns received / spent / spendable totals and optional per-note breakdown. Auto-detects birthday for Zcash.

Derive view key

FREE · 6/min/IP · Zcash UFVK from a phrase

POST /v1/private/derive-viewkey — forwards a BIP-39 phrase to the local orchard-scanner. Try it in-page below.

1. Pay $0.10 x402 (USDC on Base) /v1/private/watch 2. Watch created credit = $0.10 view key encrypted at rest 3. Credit drains – $0.02 per UTC day (idle) – $0.005 per webhook poller billed each tick (3 min default) 4. Webhook delivered HMAC-SHA256 signed body includes credit block 5. Low-credit warning fires once when credit < $0.05 6. Top up any amount $0.10 / $1 / $5 via x402 re-arms low-credit warning
Lifecycle of one Private Watch. Credit ticks down per day and per webhook; top-ups extend the meter at any time.

Try the free derive endpoint in-page (no x402 payment, no curl)

Calls POST /v1/private/derive-viewkey directly from this page. Returns a read-only Unified Full Viewing Key (UFVK) that you can plug into /v1/private/watch or /v1/private/historical.

Security warning. Your seed phrase will be sent over TLS to api.seneschal.space. We do not log or persist it, but a network attacker between you and us would see the bytes. For maximum safety, derive offline using the orchard-scanner binary on a trusted machine. If you only want to see it work, paste the canonical BIP-39 vector abandon abandon … abandon art (24 words) — it’s a public test vector and grants nothing.
Looking for the ERC-4337 paymaster? Pay gas in USDC / EURC / USDT / DAI / cbBTC on Base mainnet via the Seneschal paymaster — verified on BaseScan + Sourcify, smoke-tested via Pimlico, no SDK lock-in. Paymaster integration docs »

Why this exists

Seneschal runs an Ethereum block builder and a vertically-integrated liquidation searcher. As a side effect of doing our own work we continuously track ~500 Morpho borrowers, 1,300+ Spark borrowers, every Aave V3 mainnet position with non-trivial debt, and the winning builder of every slot since May 2026. Nobody else publishes this combination, so we expose it.

Two protocols, same backend:

Quick start

REST

curl https://api.seneschal.space/v1/liquidations/atrisk?max_hf=1.05

MCP (Claude Desktop / Cursor)

Add this to your MCP client config (e.g. ~/.cursor/mcp.json):

{
  "mcpServers": {
    "seneschal-data": {
      "url": "https://mcp.seneschal.space/"
    }
  }
}

The full Seneschal tool surface becomes available to your agent: seneschal_health, seneschal_list_at_risk_borrowers, seneschal_list_borrowers, seneschal_recent_liquidations, seneschal_get_borrower, seneschal_get_borrower_history, seneschal_builder_leaderboard, seneschal_stats_overview, seneschal_flashloan_providers, seneschal_q (Penny Oracle dispatcher), plus the Private Watch suite: seneschal_private_watch_info, seneschal_private_watch_create, seneschal_private_watch_topup, seneschal_private_watch_topup_crypto, seneschal_private_watch_historical, seneschal_private_watch_derive_viewkey, seneschal_private_watch_status, seneschal_private_watch_cancel and seneschal_private_watch_test.

REST endpoints

GET/v1/health

Service liveness plus row counts and data-source mtimes. Useful for both monitoring probes and confirming freshness before consuming downstream data.

GET/v1/liquidations/atrisk

Current snapshot of borrowers near liquidation, sorted by ascending health factor.

Query paramTypeDefaultNotes
protocolenum(all)aave, morpho, spark, or compound
max_hfnumber(none)Return only positions with health factor strictly below this.
min_debt_usdnumber0Filter out dust.
limit1..50050Max rows.
GET/v1/liquidations/recent

Liquidations observed in the recent past. Each row's outcome field is either won_by_other (a competitor liquidator landed) or we_landed (Seneschal's own searcher won it).

Query paramTypeDefaultNotes
since_msepoch msnow − 24h
protocolenum(all)
limit1..50050
GET/v1/borrowers

Discovery endpoint: list borrowers from Aave / Morpho without knowing addresses in advance. Supports HF range, debt range, sort field/direction and offset pagination.

Query paramTypeDefaultNotes
protocolenum(all)aave, morpho, compound
min_hfnumber(none)Inclusive lower bound.
max_hfnumber(none)Exclusive upper bound.
min_debt_usdnumber0
max_debt_usdnumber(none)
sort_byenumhealth_factorhealth_factor, debt_usd, collateral_usd, last_observed_ms
sort_direnumascasc or desc
limit1..50050
offsetinteger0For pagination.
GET/v1/borrowers/:address

Combined snapshot for one borrower across every protocol where we have data.

GET/v1/borrowers/:address/history

Per-block historical health-factor traces for one borrower.

Query paramTypeDefaultNotes
protocolenum(required)aave or morpho (Spark history not retained as SQL).
since_msepoch msnow − 7d
until_msepoch msnow
granularityenumrawraw, hour, or day
limit1..500500
GET/v1/builders/leaderboard

Block-builder market share derived from our own slot-by-slot shadow recorder. Cached for 60s.

Query paramTypeDefaultNotes
windowenum24h24h, 7d, 30d, all
limit1..50020Top-N builders to return.
GET/v1/stats/overview

Single bundled endpoint feeding the public stats dashboard: totals, health-factor histogram, top-10 at-risk, 30-day liquidation counts, builder share for 24h/7d/30d, and recent liquidations — all in one call so the dashboard renders in one round trip.

GET/v1/flashloan/providers

Curated catalogue of Ethereum mainnet flash-loan providers (Aave V3, Balancer V2, Morpho Blue, Uniswap V3, FlashBank) with current fee in basis points, contract addresses, qualitative liquidity notes, and per-provider caveats. Helpful for searchers picking the cheapest viable provider for a liquidation.

Query paramTypeDefaultNotes
chainstringethereumOnly ethereum is catalogued today.
max_fee_bpsnumber(none)Drop providers whose flat fee exceeds this (1bp = 0.01%).
multi_assetbool(any)If true, only return providers that support borrowing multiple assets in one flash loan.

Worked example

Find the most at-risk Aave borrower right now and pull their HF history:

$ curl -s 'https://api.seneschal.space/v1/liquidations/atrisk?protocol=aave&limit=1' | jq '.results[0]'
{
  "borrower": "0x...",
  "protocol": "aave",
  "health_factor": 1.011,
  "collateral_usd": 8412055,
  "debt_usd": 8240015,
  "liquidatable": false,
  ...
}

$ curl -s "https://api.seneschal.space/v1/borrowers/0x.../history?protocol=aave&granularity=hour"
{ "address": "0x...", "points": [ ... 168 hourly observations ... ] }

MCP tool reference

Identical surface to REST: every endpoint has a matching tool with the same parameters and same response shape (just wrapped as MCP tool content).

ToolUse
seneschal_healthLiveness + freshness probe.
seneschal_list_at_risk_borrowersFind liquidatable positions across all DeFi.
seneschal_list_borrowersDiscovery / pagination over the full borrower set with HF + debt range filters.
seneschal_recent_liquidationsRecent on-chain liquidation events.
seneschal_get_borrowerLatest state of one borrower across protocols.
seneschal_get_borrower_historyTime-series HF traces.
seneschal_builder_leaderboardEthereum builder market share.
seneschal_stats_overviewOne-call aggregate bundle powering the public dashboard.
seneschal_flashloan_providersCurated catalogue of mainnet flash-loan providers (Aave, Balancer, Morpho, Uniswap V3, FlashBank).
seneschal_paywall_infoFree. Describes the live x402 paywall (network, recipient, per-call price, MCP tool name) for the premium tier.
seneschal_premium_opportunitiesPremium. Uncapped at-risk catalogue ranked by expected value, with realised 7d market intel. Paid via x402 at the REST surface.

Premium tier (x402) — LIVE

Pay-per-call endpoints on Base mainnet, settled in USDC via the x402 protocol. Three price tiers — bulk feeds, builder analytics, and atomic single-fact queries:

EndpointPriceWhat you get
GET /v1/premium/opportunities $0.05 Uncapped at-risk borrower catalogue plus realised 7-day market intel (top liquidators, per-market win rate, our own attempt outcomes), ranked by expected liquidation value. Honest value-add over the free 500-row, snapshot-only /v1/liquidations/atrisk.
GET /v1/premium/builder-stats $0.10 Per-builder bid distribution (p25/median/p75/p90/p99/max ETH) plus a 24-element hourly slot histogram over a configurable window. Answers "what value do I need to land in builder X's bundle right now?" for searchers tuning bundle pricing. Sourced from our own shadow recorder so it covers every observed slot, not just landed blocks.
GET /v1/premium/counter-mev $0.05 Counter-MEV / approval-risk feed. Uncapped, risk-scored set of malicious approval-harvesting spender contracts (the JaredFromSubway $7.5M drain pattern — standard ERC-20 approvals, not a contract-gate bypass), honeypot/bait tokens (fake fWETH/fUSDC lookalikes, Salmonella-style fee-on-transfer & sell-revert traps), and live dangling approvals at risk. Filter ?category=&min_score=&limit=. Free summary + teaser at GET /v1/counter-mev/summary; live dashboard at counter-mev.seneschal.space.
GET /v1/q/* (15 endpoints) $0.001 Penny Oracle. Atomic single-fact endpoints — each answers ONE yes/no or one number, returns in <50 ms, and is designed for tight agent loops where bulk JSON is overkill. See the catalogue below.
POST /v1/private/watch $0.10 Private Watch — create. Subscribes a Monero or Zcash address to view-key based payment monitoring. $0.10 buys the watch + $0.10 of opening credit. Credit meter runs at $0.02/day idle + $0.005/webhook delivered. View keys are AES-256-GCM encrypted at rest with an operator-supplied master key. See private watch below.
POST /v1/private/topup — $0.10
POST /v1/private/topup-1 — $1.00
POST /v1/private/topup-5 — $5.00
POST /v1/private/topup-custom — any $0.10–$25
tiered Top up an existing watch (USDC via x402). Explicit tiers so the x402 paywall can quote each price up front. $0.10 buys ~5 idle days, $1.00 buys ~50, $5.00 buys ~250; topup-custom takes any amount. Body: {watchId, watchToken} (plus amountAtomic for custom).
POST /v1/private/topup-crypto
GET /v1/private/topup-crypto/:quoteId
FREE quote
(pay in coin)
Top up by paying in Monero/Zcash. The POST is free and returns a quote — a receiving address, the exact coin amount to send (Monero: a unique-amount invoice tag; Zcash: a memo), and a USD rate locked for ~15 min. Send the payment, then poll the GET (header x-watch-token) until status=settled. We detect it with our own view-key scan and credit the watch after the stated confirmations; we never hold a spend key. Body: {watchId, watchToken, chain, amountUsdCents} ($2–$500).
POST /v1/private/historical $0.50 One-off scan. Returns all spendable + spent notes for a view key without setting up a watch. View key streams to NFPT in memory only — never written to our SQLite or logs. Body: {chain, address, viewKey, birthdayHeight?, toHeight?, includeNotes?}.
POST /v1/private/derive-viewkey FREE BIP-39 → UFVK derivation (Zcash). Rate-limited to 6/min/IP. Forwards a 12- or 24-word seed phrase to NFPT’s orchard-scanner CLI and returns the matching read-only Unified Full Viewing Key. Loud security warning in the response — the phrase transits our server (no logging, no persistence) but a network observer between you and us would see the bytes; derive offline if you can.

Penny Oracle — /v1/q/* atomic-fact endpoints

Seventeen micro-priced endpoints, all GET, all $0.001 per call (1/100th of a cent). Each returns a flat object so an agent can branch directly on the answer without nested-field gymnastics. The free catalogue at GET /v1/q enumerates every question and its input parameters.

EndpointInputsAnswers
/v1/q/liquidatableaddr, protocol?Is borrower X liquidatable right now? Returns {found, liquidatable, hf, debt_usd, last_seen_ms} across Aave + Morpho.
/v1/q/at-risk-countmax_hf?, min_debt_usd?, protocol?Count + total debt of borrowers below the named HF and above the named debt floor.
/v1/q/recent-liquidationssince_min?, protocol?Number of liquidations observed in the last N minutes and their aggregate debt.
/v1/q/top-builderwindow?Highest-share builder in the named window (24h/7d/30d).
/v1/q/builder-sharebuilder, window?Slot share of a specific builder (substring match) over the window.
/v1/q/builder-bidbuilder, pct?, window?Percentile bid value (in ETH) for that builder over the window. Use pct 25/50/75/90/99.
/v1/q/block-valuewindow?, pct?Network-wide percentile block value (proposer payment, ETH) across all winning slots in the window — the "is it worth bundling?" number.
/v1/q/cheapest-flashloanasset, chain?Cheapest flash-loan provider for that asset on that chain. Defaults to Ethereum mainnet.
/v1/q/data-freshnesssourceAge in seconds of the freshest row in one of: shadow_blocks, borrower_snapshot, morpho_borrower_snapshot, missed_liquidations, executions.
Privacy-chain facts — sourced from Seneschal-operated full nodes
/v1/q/xmr/heightCurrent Monero chain height + sync state. Sourced from a synced monerod v0.18 instance.
/v1/q/xmr/mempoolNumber of pending transactions in the Monero mempool right now.
/v1/q/xmr/feeRecommended per-byte fee in piconero (also per-kB) so wallets can budget the next-block tx.
/v1/q/xmr/fee-estimateActionable Monero fee estimate: per-byte fee × a typical 1500-byte tx, broken out by priority level, in piconero and XMR.
/v1/q/xmr/last-blockTimestamp + age of the most recent Monero block, plus hash, difficulty, size.
/v1/q/zec/heightCurrent Zcash chain height + verification progress + best block hash. Sourced from a synced zebra node.
/v1/q/zec/mempoolZcash mempool count + bytes.
/v1/q/zec/last-blockTimestamp + age of the most recent Zcash block, plus hash, difficulty, size.

The privacy-chain set is the "joining the dots" of this product line: Monero and Zcash are the two chains where running a node yourself is genuinely awkward (~108 GB and ~270 GB respectively, plus syncing hours), public RPC endpoints rate-limit aggressively, and there is no Etherscan-style hosted explorer to fall back on. We run synced nodes already; the x402 sub-cent paywall finally makes selling individual reads worth the trouble. All responses are cached server-side for 10 s so an agent in a tight loop costs the daemon nothing extra.

Hand-rolled flow:

curl -s 'https://api.seneschal.space/v1/q/liquidatable?addr=0x...&protocol=aave'
# Pays $0.001 USDC on Base via x402-fetch and returns
# {"found":true,"protocol":"aave","liquidatable":false,"hf":1.03,"debt_usd":24500,"last_seen_ms":1716314400000}

MCP agents can use the single dispatcher tool seneschal_q with {question, params} rather than wiring up separate tool calls per question. The MCP path is currently free; the HTTP path is paywalled.

Payment uses the x402 protocol (spec v2). Unsigned requests get HTTP 402 with a base64-encoded Payment-Required header carrying machine-readable PaymentRequirements; the client signs an EIP-3009 transferWithAuthorization off-chain and retries with a X-PAYMENT header. We delegate verify + settle to Coinbase’s CDP facilitator, which broadcasts the EIP-3009 transfer on Base. No API keys for the payer, no subscriptions, no gas for the payer — the facilitator pays the gas. (Settling through Coinbase also lists the service in the Coinbase x402 Bazaar.)

Free metadata endpoint for budgeting before any paid call:

curl https://api.seneschal.space/v1/paywall
# → {"protocol":"x402","network":"eip155:8453",
#    "facilitator":"https://api.cdp.coinbase.com/platform/v2/x402",
#    "facilitator_mode":"cdp",
#    "payTo":"0x46Ba634261566CF242c853d1f49511f9268ba674",
#    "scheme":"exact (EIP-3009 transferWithAuthorization)",
#    "routes":[{"endpoint":"GET /v1/premium/opportunities","price":"$0.05",...},
#              {"endpoint":"GET /v1/premium/builder-stats","price":"$0.10",...},
#              {"endpoint":"GET /v1/q/liquidatable","price":"$0.001",...}, … ]}

Agents using x402-axios, x402-fetch, @x402/express, @x402/fastify or the official client libraries pay automatically on 402. Hand-rolled flow, in three lines of TypeScript:

import { wrapFetchWithPayment } from 'x402-fetch';
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';

const wallet = createWalletClient({ account, transport: http(), chain: base });
const fetchPaid = wrapFetchWithPayment(fetch, wallet);
const r = await fetchPaid('https://api.seneschal.space/v1/premium/opportunities');
console.log(await r.json());

The account is any viem-compatible signer holding a small USDC balance on Base — a fresh EOA + 1 USDC will fund ~20 paid calls.

Private Watch — /v1/private/* (XMR/ZEC payment monitoring)

This is the headline product. The agent (or merchant, or wallet) hands us a public address, a view key, and a webhook URL. We scan every new block against the view key on the local NFPT wallet-scanner (driven by monerod for Monero and zebra + the Zcash orchard-scanner binary for Zcash) and POST a signed event to the webhook the moment the balance changes.

Pricing — prepaid credit meter. One payment of $0.10 opens a watch and seeds $0.10 of credit. The watch then drains at $0.02 per UTC day idle (proportional, billed each poll tick) plus $0.005 per webhook delivered. When credit hits a low threshold ($0.05) we POST a one-shot low_credit warning so your receiver can top up before going silent. Top up any time with one of three paywalled paths ($0.10 / $1 / $5). Credit balance is in every webhook body so well-behaved receivers can ignore the dedicated event.

EndpointMethodAuthDescription
/v1/private/watchPOSTx402 $0.10Create a watch with $0.10 of starter credit. Body: {chain, address, viewKey, webhookUrl, birthdayHeight?}. Returns {watchId, watchToken, webhookSecret, expiresAt, creditAtomic, ratePerDayAtomic, ratePerCallAtomic, topupEndpoints, testEndpoint}. durationDays is silently ignored for backward compatibility — the credit meter is the only knob now.
/v1/private/topupPOSTx402 $0.10Adds $0.10 of credit (~5 idle days). Body: {watchId, watchToken}.
/v1/private/topup-1POSTx402 $1.00Adds $1.00 of credit (~50 idle days).
/v1/private/topup-5POSTx402 $5.00Adds $5.00 of credit (~250 idle days).
/v1/private/historicalPOSTx402 $0.50One-off historical scan. Returns spendable + spent + total-received totals (and optionally per-note breakdowns). View key streams to NFPT in-memory only — never written to our SQLite or logs.
/v1/private/derive-viewkeyPOSTFREE (6/min/IP)Zcash only. Derives a UFVK from a BIP-39 mnemonic via NFPT’s orchard-scanner. Phrase transits in-memory; we do not log or persist. Use offline derivation where possible.
/v1/private/watch/:idGETheader x-watch-tokenRead current status: credit block (remaining / days_remaining / low-credit flag), last polled time, delivery count, last event type, last known balance snapshot, ETA to next poll. Free.
/v1/private/watch/:idDELETEheader x-watch-tokenCancel a watch (any remaining credit is forfeited — this is by design to avoid refund accounting and chargebacks). Free.
/v1/private/watch/:id/testPOSTheader x-watch-tokenFires one synthetic webhook so you can verify your signature handling and receiver before relying on real payments. Stamped event: synthetic_test so well-behaved receivers can branch and skip processing. Free.
/v1/private/infoGETFree metadata: current prices, supported chains, top-up tiers, upstream NFPT health, security notes.
/v1/private/healthGETCounters only (no PII) for ops monitoring — includes credit aggregates.

Webhook contract. Every delivery is a POST with content-type: application/json and three Seneschal headers:

X-Seneschal-Watch-Id: <uuid>
X-Seneschal-Event: balance_change | scan_complete | status_change | low_credit | synthetic_test
X-Seneschal-Signature: sha256=<HMAC-SHA256(webhookSecret, exact request body)>

The body is plain JSON. Every delivery (including low_credit warnings) carries a credit block so receivers always know where they stand:

{
  "watchId": "fb1a29bd-…",
  "chain": "monero",
  "address": "46FzbF…",
  "event": "balance_change",
  "timestamp": "2026-05-21T08:06:54.626Z",
  "nonce": "mpf7j7he-30s7yx",
  "previous": { "balanceAtomic": "0", "scannedHeight": 3349800, … },
  "current":  { "balanceAtomic": "1234567890", "scannedHeight": 3349881, … },
  "delta": {
    "balance_atomic": "1234567890",
    "before_atomic": "0",
    "after_atomic":  "1234567890"
  },
  "credit": {
    "remaining_atomic": "94750",
    "remaining_usd": "0.0948",
    "days_remaining": 4.7,
    "low_credit": false,
    "rate_per_day_atomic": "20000",
    "rate_per_call_atomic": "5000",
    "low_credit_threshold_atomic": "50000"
  }
}

The low_credit event is fired once when remaining_atomic first drops below the threshold ($0.05 of credit, ~2.5 idle days). Top up >= the threshold to re-arm the warning. After credit reaches zero the watch stops being polled entirely; no further deliveries happen until the next top-up.

Receivers verify the signature with the per-watch webhookSecret returned at creation. Constant-time HMAC comparison defeats forgery; the secret is generated server-side and never reused across watches. Bodies are capped at 64 KB.

Security model. View keys decrypt incoming transactions but cannot spend — you can hand us one without giving up custody. We nonetheless encrypt the key at rest with AES-256-GCM under an operator-held master key (PRIVATE_WATCH_ENCRYPTION_KEY); plaintext only exists in memory while the poller is decrypting it for the next scan call. Webhook URLs are vetted at creation:

Tunables. Polling cadence is 3 min by default (stays inside NFPT's 5 min scanner idle window). Webhook receivers get an 8 s HTTP timeout and we cap drained response body at 4 KB to defeat slow-loris megabyte responses. On webhook failure we retry on the next tick; 50 consecutive failures retire the watch with a dead flag. After expiry the row is hard-deleted on the next tick.

Zcash birthday height. If omitted we default to NU6 (3,042,000) so the scanner never trips into a multi-hour autoDetect backwards-walk. Wallets created before April 2024 should pass an explicit birthdayHeight matching wallet age.

MCP. Agents already inside a paid MCP session can use seneschal_private_watch_create, seneschal_private_watch_topup, seneschal_private_watch_topup_crypto, seneschal_private_watch_historical, seneschal_private_watch_derive_viewkey, seneschal_private_watch_status, seneschal_private_watch_cancel and seneschal_private_watch_info for end-to-end flow without direct HTTP.

Historical lookups & view-key derivation

Two new on-demand services let agents recover state without committing to a long-running watch:

Data freshness & coverage

Limits, terms, fair use

About

Seneschal is a single-operator Ethereum block builder and searcher running an rbuilder fork from a co-located server in Helsinki. Builder on-chain extra_data is Seneschal/0.1. Contact @OrknetP on Telegram. By using the service you agree to the Terms and Privacy policy.