Generate your Agent API Key
Free users can generate a read-only Agent API Key for MCP tools with signals:read and 200 MCP requests/day. Pro adds real-trade REST/MCP scopes; Elite adds full agent write access.
PolyTrackers API (External + AI Agent Guide)
This is the production-facing API guide for external developers and AI agents.
- OpenAPI JSON:
/openapi.json - Agent instructions:
/llms.txtand/skill.md - MCP endpoint:
https://polytrackers.com/api/mcp
Scope
This guide/spec covers the full AI agent surface:
- Auth:
/api/auth/refresh - Account/Tier:
/api/account,/api/account/stats,/api/account/streak,/api/account/risk-profile - Discovery:
/api/markets,/api/market-signals,/api/leaderboard,/api/clob/price,/api/trader-lookup - Anomalies:
/api/anomalies,/api/anomalies/{id},/api/anomalies/performance,/api/scan/trigger,/api/scan/run - Backtesting:
/api/backtest - Recommendations, activity & feedback:
/api/recommendations,/api/bets/sync,/api/feedback - Agent API Keys:
/api/keys,/api/keys/generate, plus public feeds - Trading:
/api/trades,/api/trade/execute,/api/trade/history,/api/trade/ledger,/api/trade/money-summary,/api/trade/orders* - Alerts & webhooks:
/api/alerts/preferences,/api/webhooks* - Whale service + ingest webhooks: wallets/roster/whale control +
/api/whale/webhook*
Excluded intentionally:
/api/mock/*internal/MCP-only simulation helpers (use the registeredpt_mock_*tools)/api/cron/*scheduled internal jobs/api/admin/*,/api/stripe/*,/api/telegram/*,/api/onboarding/*and other UI/operator-only routes
The MCP-only pt_whale_forward_returns_get read accepts 1–50 arbitrary wallet
entries with exact per-wallet since timestamps and a shared as_of cutoff.
It returns one request-ordered row per entry with bounded fill coverage,
gross/post-cost capital-accounting ROI, unresolved capital, resolution
provenance, and isolated ok|partial|error state. Its Data API and Gamma calls
use the low-priority backfill request budget and are counted in _budget.
Zero resolved capital produces null ROI; compute cohort medians client-side
over non-null rows. Re-runs at the same cutoff may incorporate later
authoritative Gamma outcome/closedTime evidence as corrected upstream data.
The Elite, read-only pt_copy_replication_aggregates_get tool is MCP-only,
operator-allowlisted through ADMIN_EMAILS, and has no public REST or browser
route. Ordinary customer keys fail closed. It requires one exact whale, fixes
the service-role Copy Receipt V1 RPC limit at one, and uses index-backed lookup
with a caller-side five-second deadline that aborts the PostgREST request.
Scored metrics stay null below 20
distinct settled conditions and high confidence still requires 40+ conditions
with at least 75% complete receipts. Do not present it as a public copy score
or recommendation.
GET /api/recommendations includes the stored botLikeness aggregate. A
high tier adds a copy-fill-lag warning and demotes matchScore; the response
never includes raw Polymarket trade history.
Rows also include tradingStyle, a deduped BUY entry-price
distribution with sample/window metadata. Its badge classification is withheld
below 20 fills; sufficiently sampled distributions also contribute to
deterministic risk-fit scoring.
Recommendation rows also expose name_source (tracked_alias, leaderboard,
or address_fallback), name_as_of, and Polymarket's rename-stable
pseudonym when available. Address-prefixed legacy aliases are treated as
absent; wallet address remains the identity key.
GET /api/leaderboard and GET /api/recommendations include upstream
metadata for the shared Polymarket leaderboard fetch. Check its per-timeframe
outcomes plus degraded and stale before interpreting an empty candidate
pool. A stale response is explicitly labeled, retains the original fetchedAt
timestamp, and is bounded to the latest fully healthy snapshot from the prior
24 hours. staleExpiresAt is the absolute cutoff; public, REST, and MCP
response layers do not cache leaderboard payloads beyond the durable
10-minute Redis cadence, and each response revalidates after downstream
enrichment so an embedded fallback is removed at that cutoff. Candidate
actionability also distinguishes known dormancy from missing ingestion
coverage: zero local activity inside the warning window for an untracked,
newly tracked, or recently resumed wallet returns warning-only
ACTIVITY_COVERAGE_UNKNOWN plus
recent_activity.source/coverage metadata, never DORMANT_WHALE. MCP
capability revisions are roi-sample-provenance-v6 for the leaderboard,
recommendations-prior-removal-v5 for recommendations, and
digest-prior-removal-v13 for the copy-trading digest. Wallet-shaped suggestion/trader rows use the canonical
recent-flow majority predicate, filter majority-ephemeral and unknown-coverage
rows under durable_only, retain dormant wallets, and expose bounded
durability_profile provenance.
Recommendation and digest-suggestion rows expose
previously_removed_by_caller by normalized wallet address. It is null or
contains { count, last_removed_at, last_reason }, where count covers
currently inactive assignments across the caller's mock wallets rather than
lifetime removal cycles. These candidates remain visible with a warning by
default. REST accepts exclude_previously_removed=true|false; direct MCP
accepts the boolean exclude_previously_removed and binds it to continuation
cursors. Filtered counts appear as omitted_by_prior_removal_filter.
PATCH /api/roster/{address} treats fromWalletId as the source-assignment
selector. If one address has active assignments on multiple visible wallets,
an address-only write fails with HTTP 409, code AMBIGUOUS_ASSIGNMENT, and
candidate_wallet_ids rather than changing an arbitrary row.
POST /api/roster accepts any exact Polymarket whale wallet address; the whale
does not need to appear in leaderboard or recommendation results. The
walletId may select a caller-owned paper roster or dedicated real-money copy
roster returned by GET /api/wallets. name is optional, and
copyEnabled:false stages the assignment watch-only. When starting from a
username, resolve and review the exact address with GET /api/trader-lookup
before creating the assignment. Username resolution uses Polymarket's public
profile search, then fetches and validates trades for only that canonical
wallet; trade pages are never used to infer username ownership.
Auth modes
1) User auth (session cookie or bearer)
Most user-scoped agent endpoints accept authenticated user context via:
- Supabase session cookie (
sb-*managed cookies) Authorization: Bearer <supabase-jwt-access-token>
2) JWT-only bearer
POST /api/keys/generate strictly verifies Supabase JWT bearer identity.
3) Agent API Key bearer
Agent API Keys are prefixed ptk_ and are passed as Authorization: Bearer <agent-api-key>. The same key works for PolyTrackers MCP server access and direct REST/API requests when it has the required scopes:
signals:read— read-only dataagent:scan— scan trigger where allowedtrade:execute— real trade execution only, after readiness and preflight gates passagent:full— full agent/write/trade access (currently Elite-only for Agent API Keys)
Endpoints that accept Agent API Keys:
| Endpoint | Required scope | Direct REST tier |
|---|---|---|
GET /api/anomalies | signals:read | Pro+ |
GET /api/trades | signals:read | Free+ |
GET /api/leaderboard | signals:read | Pro+ |
POST /api/backtest | signals:read | Free+ (fixed demo config on Free) |
GET /api/anomalies/performance | signals:read | Pro+ |
GET /api/public/whale-signals | signals:read | Elite |
GET /api/public/copy-signals | signals:read | Elite |
POST /api/trade/execute | trade:execute | Pro+ |
GET /api/trade/history | agent:full | Free+ |
GET /api/trade/ledger | agent:full | Free+ |
GET /api/trade/money-summary | agent:full | Free+ |
GET /api/trade/orders, GET /api/trade/orders/{id} | agent:full | Elite |
DELETE /api/trade/orders/{id} | agent:full | Pro+ |
POST /api/feedback | agent:full | Elite |
The direct REST tier applies only when the bearer is a ptk_ Agent API Key;
session-authenticated callers follow the tier behavior below. Free Agent API
Keys work on POST /api/backtest, but only with the fixed demo configuration,
and the returned trade list is capped at 5 rows. New agent:full keys are
normally issued only to Elite accounts; the Pro+ cancellation tier allows an
existing agent:full key to keep reducing exposure after an account downgrade.
4) Service auth
For whale-service control endpoints:
x-whale-service-secret: <secret>orAuthorization: Bearer <WHALE_SERVICE_SHARED_SECRET>
5) Scan secret
POST /api/scan/run also accepts x-scan-secret: <SCAN_SECRET> (bypasses the tier gate; used by cron/internal automation).
6) Webhook signature auth
POST /api/whale/webhook requires:
x-alchemy-signature = HMAC_SHA256(raw_body, ALCHEMY_WEBHOOK_SIGNING_KEY)
When a registration has a secret, outbound deliveries from /api/webhooks*
include X-Webhook-Signature with the value
sha256=<HMAC_SHA256(raw_body, secret)>. Compute the HMAC over the raw request
body before parsing it.
Tier gating (important)
Effective tier resolution: a user on free with an active trial (trial_ends_at > now) is treated as pro. Tier failures return TIER_UPGRADE_REQUIRED with the required tier and an upgradeUrl.
Free
GET /api/trades: 7-day mock history plus full-depth own real-trade receipts.GET /api/anomalies: max 5 results, 7-day base retention window — each activated referral adds 7 days, up to 28 days total — with a 1-hour visibility delay (detections newer than 1 hour are hidden).POST /api/scan/trigger: not allowed (403)./api/keys+POST /api/keys/generate: allowed; generated keys always get exactlysignals:read(requested write/automation scopes are stripped). Limit: 1 active Agent API Key per user; rotate by revoking the old key, then generating a replacement. MCP usage is capped at 10 requests/min per tool and 200 requests/day total.POST /api/trade/execute: session callers can execute only when manual trading-readiness checks pass. Agent API Key/MCP execution is unavailable because Free keys never carrytrade:execute.- Session callers can read their own
/api/trade/history,/api/trade/ledger, and/api/trade/money-summary; direct Agent API Key calls still requireagent:full. Order listing/detail remain unavailable.DELETE /api/trade/orders/{id}may cancel the signed-in user's own resting order because cancellation reduces exposure. /api/webhooks(create): not allowed (403)./api/public/whale-signals,/api/public/copy-signals: not allowed (403).
Pro
GET /api/trades: 90-day mock history plus full-depth own real-trade receipts.GET /api/anomalies: max 100 results, 90-day retention window, no delay.POST /api/scan/trigger: allowed, rate limited to 2/hour./api/keys+POST /api/keys/generate: allowed; scopes default tosignals:read, with optionaltrade:executefor real trade execution andagent:scanfor scan automation where allowed. Limit: 1 active Agent API Key per user; rotate by revoking the old key, then generating a replacement./api/webhooks(create): up to 5 active webhooks./api/public/whale-signals,/api/public/copy-signals: not allowed (Elite only).POST /api/trade/execute: session callers can execute only when manual trading-readiness checks pass. Agent API Key/MCP execution requirestrade:executescope.- Session callers can read their own
/api/trade/history,/api/trade/ledger, and/api/trade/money-summary; direct Agent API Key calls still requireagent:full. Order listing/detail remain unavailable.DELETE /api/trade/orders/{id}may cancel the signed-in user's own resting order.
Elite
GET /api/tradesreturns full-depth mock and own real-trade history.GET /api/anomalies: max 200 results, full retention window.POST /api/scan/trigger: allowed, rate limited to 10/hour./api/keys+POST /api/keys/generate: scopes may includesignals:read,trade:execute,agent:scan, and/oragent:full. Limit: 1 active Agent API Key per user; rotate by revoking the old key, then generating a replacement./api/trade/execute,/api/trade/history,/api/trade/ledger,/api/trade/money-summary, and/api/trade/orders*: allowed where the route-specific readiness checks pass (trade:executeoragent:fullfor execute API-key auth). Browser/session order cancellation is allowed for the signed-in user's own resting order regardless of tier./api/public/whale-signals,/api/public/copy-signals: allowed./api/webhooks(create): up to 50 active webhooks.
Rate limits
Agent API Key REST limits use the scope shown below; MCP separately applies per-identity/per-tool and aggregate request buckets (see MCP rate limits). Session requests are limited per user, while the public discovery and refresh limits are per IP.
| Endpoint | Limit | Scope |
|---|---|---|
POST /api/auth/refresh | 30/minute | IP |
| Anonymous discovery GETs | shared 60/minute anonymous budget | IP |
POST /api/scan/trigger | Pro: 2/hour, Elite: 10/hour | user |
POST /api/keys/generate | 5/hour per user | user |
POST /api/trade/execute | 10/minute per user + 20/minute per API key | user + key |
GET /api/public/whale-signals | 120/minute per API key | API key |
GET /api/public/copy-signals | 120/minute per API key | API key |
The shared anonymous discovery budget covers GET /api/markets,
GET /api/events, GET /api/home-snapshot, and
GET /api/market-signals. One anonymous caller consumes the same IP-scoped
60-request bucket across all four routes.
Rate limit response headers
Every rate-limited response — a success as well as a 429 — carries the RFC
RateLimit-* quota view for the bucket that governed it. Read it and pace
yourself; do not discover the ceiling by being refused.
| Header | Meaning |
|---|---|
RateLimit-Limit | Effective ceiling for this caller, after tier multipliers (Pro x2, Elite x5). |
RateLimit-Remaining | Requests left in the current window. |
RateLimit-Reset | Delta-seconds until the quota refills. Never a unix timestamp. |
RateLimit-Policy | "bucket";q=<limit>;w=<window seconds> — endpoints sharing a bucket share one budget. |
curl -si https://polytrackers.com/api/markets | grep -i '^ratelimit-'RateLimit-Limit: 60
RateLimit-Remaining: 59
RateLimit-Reset: 60
RateLimit-Policy: "public-market-read";q=60;w=60
Rules a client can rely on:
- On a
429,Retry-Afterstays the authoritative back-off signal, andRateLimit-Resetis never smaller than it. Sleeping forRateLimit-Resetis therefore always safe. RateLimit-Resetrounds up, so it never expires early.- Paths with no limiter behind them omit the headers entirely rather than
advertise a quota nobody enforces:
/api/health,/api/cron/*, the signed provider webhooks, and the cachedGET /api/mcpcapability probe. - Two limiters signal with
Retry-Afterand a structured body but publish no quota view: thePOST /api/trade/executeper-API-key limiter, andPOST /api/auth/refresh(whose429can be proxied from upstream). Treat a missing quota view as "unknown budget", never as "unlimited". - The MCP per-user/per-tool buckets are metered in-route and report
retry_after_secondsinside the JSON-RPC error payload; theRateLimit-*headers on/api/mcpdescribe the per-IP transport cap only.
MCP rate limits
MCP (POST /api/mcp) is metered separately from the REST limits above, in
three per-user layers plus one transport cap. All apply at the same time; the
tightest one binds.
| Bucket | Free | Pro | Elite | Scope |
|---|---|---|---|---|
Per-tool tools/call | 10/minute | 60/minute | 300/minute | user, per tool name |
Daily tools/call quota (all tools) | 200/day | no daily cap | no daily cap | user |
| All JSON-RPC requests (setup + listings) | 120/minute | 600/minute | 1800/minute | user |
Transport caps on /api/mcp apply per IP before authentication, regardless
of tier: 300/minute for authenticated traffic and 60/minute for the
anonymous health probe and handshakes.
- The per-tool bucket is keyed by tool name, so a burst on one tool does not
consume another tool's budget. Blocked calls return
RATE_LIMITEDwithretry_after_seconds. - The daily quota is consumed only after the per-minute bucket allows the call, so minute-throttled calls do not burn the daily budget.
- The request bucket charges every authenticated POST one setup unit, plus one
extra unit for catalog listings and resource/prompt reads.
tools/call,initialize,ping, andnotifications/*pay only the setup unit. - The transport IP caps are enforced before JSON-RPC handling and may return
a non-JSON 429; treat any raw 429 as
RATE_LIMITEDand back off.
The same figures are published machine-readably in the
polytrackers://mcp/capabilities resource (rate_limit_per_minute,
rate_limit_per_day, transport_rate_limits.streamable_http).
404 response shape (unknown paths)
Any /api/** path that matches no endpoint returns HTTP 404 with a JSON body.
It is never the HTML app shell, so a client can branch on the payload:
{
"error": "Not Found",
"code": "NOT_FOUND",
"message": "No PolyTrackers API endpoint matches GET /api/nope. The OpenAPI contract lists every documented path.",
"documentation": {
"openapi": "https://polytrackers.com/openapi.json",
"apiDocs": "https://polytrackers.com/docs/api",
"llmsTxt": "https://polytrackers.com/llms.txt",
"agentSkill": "https://polytrackers.com/skill.md",
"mcp": "https://polytrackers.com/api/mcp"
}
}Send Accept: text/markdown for the same recovery links as a short markdown
document. Both variants set Vary: Accept. NOT_FOUND is not retryable —
re-read the OpenAPI contract instead of backing off.
Page (non-/api) paths keep serving the regular HTML 404 page.
Markdown content negotiation (public pages)
A curated set of public pages answers Accept: text/markdown with a short
machine-readable summary instead of HTML — the page title, its description, and
the discovery links to read next. The set is every static public page — the
homepage, the documentation hub and API reference, the guides, the comparison
pages, and the policy pages — reachable either by sending the header or by
appending .md to the path (/docs -> /docs.md). Live-data pages (the
leaderboard, anomalies, wallet tracker, analytics, search, and copy trading)
are excluded on purpose: a summary of a continuously refreshed page would carry
none of its data. public/llms.txt publishes the live list.
curl -s -H 'Accept: text/markdown' https://polytrackers.com/docs/apiRules:
- The markdown body is a summary, not a transcription. Fetch the canonical HTML URL it names when you need the full document.
- Both representations set
Vary: Accept, so a shared cache cannot mix them. - Ranking honors q-values.
text/markdownmust outranktext/html; a tie serves HTML, because a client that accepts both can render the page. - An
Acceptthat matches neither representation and carries no wildcard gets406 Not Acceptable. A missing or wildcardAcceptgets HTML. - Every other path ignores the header and serves HTML exactly as before.
429 response shape
Every 429 body includes "error" (string). The Retry-After header (seconds)
is the authoritative back-off signal — it is present on all rate-limited
responses (the one exception: 429s proxied from the upstream auth provider on
POST /api/auth/refresh may omit it). Other body fields vary by endpoint, so
treat them as optional:
| Endpoint / limiter | 429 body |
|---|---|
GET /api/public/whale-signals | { error } |
GET /api/public/copy-signals | { error } |
POST /api/trade/execute (per API key) | { error, code: "API_KEY_RATE_LIMITED", retryAfter, docsUrl } |
POST /api/trade/execute (per user) | { error, code: "RATE_LIMITED" } |
POST /api/keys/generate | { error, code: "RATE_LIMITED" } |
POST /api/auth/refresh | { error, code: "RATE_LIMITED" } |
POST /api/scan/trigger | { error } |
The fullest shape — returned by the /api/trade/execute per-API-key limiter:
{
"error": "This API key has exceeded its trade execution rate limit. …",
"code": "API_KEY_RATE_LIMITED",
"retryAfter": 7,
"docsUrl": "https://polytrackers.com/docs/api"
}Idempotency for /api/trade/execute
Trade execution is idempotent when you supply an Idempotency-Key header. For
Agent API Key requests the header is required (400 IDEMPOTENCY_KEY_REQUIRED
otherwise); session requests may still pass idempotencyKey in the body.
Every /api/trade/execute body must also include regionCertification: true
to certify the caller is permitted to trade in their jurisdiction and is not in
a restricted or prohibited Polymarket trading region.
-
Accepted formats: UUIDv4 (preferred) or any 8–256 printable-ASCII token.
-
Retrying the same key replays the original trade result (
200withidempotencyReused: true) instead of re-executing. The replay window is indefinite — keys never expire. Generate one fresh key for each new trade intent. Reuse that same key for every retry or reconciliation attempt for that intent, including after an uncertain timeout or aTRADE_IN_FLIGHTresponse; never reuse the key for a different trade intent. -
Two concurrent requests with the same key return
409 TRADE_IN_FLIGHTfor the loser:json{ "error": "trade_in_flight", "code": "TRADE_IN_FLIGHT", "idempotencyKey": "agent-trade-2026-04-20-001", "retryAfter": 5 }
Lifecycle contradictions and order-book indicators
Agent API and MCP clients must treat Gamma lifecycle fields as conservative
write gates. When pt_mock_price_get or a composed market-intel/copy-desk
workflow reports PAST_END_DATE_BUT_MARKET_APPEARS_LIVE, that is contradiction
evidence only; it is not permission to place, mirror, or override by default.
Live CLOB/order-book indicators, bid/ask/spread rows, recent trades, or
enableOrderBook signals do not override MARKET_PAST_END_DATE,
MARKET_CLOSED, auth, tier, anti-IDOR, preflight/idempotency, wallet-readiness,
region, or real-money execution safeguards.
A future manual/gated exception must prove the Gamma lifecycle field is stale, require explicit user/operator approval for the exact action, write an audit log, and must not broaden default Agent API/MCP write privileges.
Bot challenge (403)
Session traffic that BotID flags as automated receives:
{
"error": "bot_challenge_failed",
"reason": "automated_traffic_detected",
"suggestedAction": "authenticate_with_api_key",
"docsUrl": "https://polytrackers.com/docs/api"
}Valid Authorization: Bearer ptk_… requests bypass BotID entirely — see
llms.txt.
Quickstart (agent flow)
1) Sign in and obtain a session
PolyTrackers auth is passwordless. Create an account or sign in through the web
app at /register or
/login using Google OAuth or an emailed
magic link (Terms of Service and Privacy Policy acceptance is captured during
sign-up). This yields a Supabase session — a session cookie plus an
access_token / refresh_token pair you can pass as a bearer token.
Exchange a refresh token for a fresh session pair when the access token nears expiry:
curl -X POST "https://polytrackers.com/api/auth/refresh" \
-H "Content-Type: application/json" \
-d '{"refresh_token":"'"$REFRESH_TOKEN"'"}'Response includes:
access_tokenrefresh_tokentoken_typeexpires_in
2) Read account + tier
curl "https://polytrackers.com/api/account" \
-H "Authorization: Bearer $ACCESS_TOKEN"Returns tier (effective tier, honoring active trial), trialEndsAt, apiKeysCount, walletsCount, onboarding + wallet-link status.
3) Discover markets
curl "https://polytrackers.com/api/markets?query=btc&status=open&sort=volume&limit=25"To decorate public market cards with coarse recent signal evidence, pass up to 100 comma-separated condition IDs. The response contains only 24-hour whale and anomaly booleans—no wallet, trade-size, price, or execution data:
curl "https://polytrackers.com/api/market-signals?conditionIds=0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"For one market or event, the anonymous detail endpoints return more than the
list rows do. GET /api/markets/{slug} includes the full resolution-rules
description plus a heuristic resolutionRisk score; GET /api/events/{slug}
returns the event's complete outcome ladder (every priced leg, not just the
legs on a paginated list page); GET /api/events/featured returns the current
featured multi-outcome event, or {"event": null} when none qualifies:
curl "https://polytrackers.com/api/markets/some-market-slug"
curl "https://polytrackers.com/api/events/some-event-slug"
curl "https://polytrackers.com/api/events/featured"Anonymous requests to /api/markets, /api/events, /api/home-snapshot, and
/api/market-signals share one 60 requests/minute per IP discovery budget.
Pace discovery loops across the group and honor Retry-After on 429.
The {slug} detail endpoints are metered separately: each has its own
60 requests/minute per IP bucket, and both can return 503 with
reason: "upstream_request_budget_deferred" when the shared upstream request
budget defers the fetch — honor retryAfterMs before retrying.
4) Trigger anomaly scan (Pro/Elite)
curl -X POST "https://polytrackers.com/api/scan/trigger" \
-H "Authorization: Bearer $ACCESS_TOKEN"5) Read anomalies
Session, or a Pro+ Agent API Key (signals:read):
curl "https://polytrackers.com/api/anomalies?limit=50&severity=high" \
-H "Authorization: Bearer $ACCESS_TOKEN"Free-tier session callers see at most 5 rows, within their retention window (7-day base, up to 28 days with activated referral rewards), and only those detected more than 1 hour ago. Direct REST access with an Agent API Key requires Pro+.
6) Read anomaly detector performance
Aggregate win-rate, average return, and P&L for historical anomaly detections. Backed by the anomaly_performance_daily view.
curl "https://polytrackers.com/api/anomalies/performance?since=2026-01-01T00:00:00Z&until=2026-04-01T00:00:00Z&group_by=month&anomaly_type=SHARP_MOVE" \
-H "Authorization: Bearer $ACCESS_TOKEN"Query parameters:
since,until— ISO-8601 datetimes. Defaults to the last 30 days; the window is capped at 365 days.group_by— one ofday(default),month,anomaly_type,severity,all.anomaly_type,severity— optional filters passed straight through to the view.
Response body:
window—{ since, until }echoing the resolved window (ISO-8601).totals— aggregate{ flagged, resolved, wins, losses, voids, winRate, avgReturnPct, totalPnlUsd, equalDollarPnlPer100 }across the window.series— same shape astotalsbut keyed bycohort(the group_by bucket), sorted ascending.coverage—resolved / flaggedfor the window (0–1).coverageThreshold— the coverage at which the cohort is considered mature enough to surface headline numbers (currently0.5).minDecidedForHeadline— the absolute number of decided outcomes (wins + losses) that is, on its own, enough to surface headline numbers (currently20).headlineReliable—coverage >= coverageThreshold || (wins + losses) >= minDecidedForHeadline. Because markets resolve long after detection, a window can have low coverage while still holding a large, meaningful sample of decided outcomes; either condition makes the win rate reliable.
equalDollarPnlPer100 is a simulation: $100 flat-staked on every decided anomaly in the cohort, expressed as total dollars of P&L.
Accepts session auth (cookie/JWT) or a Pro+ Agent API Key with signals:read
scope. Responses are cached privately with s-maxage=60, stale-while-revalidate=300.
7) Read unified trade history
curl "https://polytrackers.com/api/trades?type=all&status=all&sort=newest&limit=25&offset=0" \
-H "Authorization: Bearer $ACCESS_TOKEN"Response body includes tier and historyDepthDays; the latter is the mock
history window (7 for Free, 90 for Pro, null for Elite). Own real-trade
receipts are full-depth at every tier. For real rows, status describes the
position: a filled BUY remains open until resolution or exit accounting is
recorded. rawStatus keeps the order lifecycle, and closedAt is only an
actual resolution/settlement time—never a bookkeeping updated_at value.
Real rows also carry failureReason and failureDetail explaining why a
failed/cancelled attempt terminated; both are null on healthy rows.
fillAmount, requestedShares, and price expose the durable execution facts;
isDustFill is derived when filled shares are below the applicable market
share minimum, executed notional is below $1, or the fill is under 10% of
requested shares. Execution reuses live market/book metadata; bounded list and
metric reads use the centralized conservative fallback and make no per-row
provider calls. Dust remains real P&L but is excluded from win-rate and
mock↔real parity grading.
payoutReconciliationStatus carries the receipt-backed payout state, while
modeledPayout is the deterministic binary/SPLIT estimate used during
reconciliation and must not be presented as observed spendable cash.
A type=real&status=executed response also includes realWalletStats. That
object is aggregated server-side over every matching executed real-trade row,
independent of limit/offset, and reports realized P&L, all-time decided-BUY
cost basis/ROI inputs, open positions, and resolved rows still awaiting P&L
attribution. The trades array remains paginated.
conditionId narrows the feed to one exact Polymarket condition ID. Offset
pagination is reliable only before offset 1000 because PostgREST caps each
source window. A page that reaches that boundary returns its reachable prefix
as an explicit terminal page with pagination.depthLimited: true,
depthLimit: 1000, and
depthLimitReason: "postgrest_max_rows"; narrow by type, status, wallet, or
condition instead of continuing.
When the wallet-scope integrity tripwire drops a foreign raw row, the returned
trade rows remain verified against the requested wallet but count metadata
cannot be proven from the same suspect result set. The response therefore adds
pagination.degraded: true,
degradedReason: "wallet_scope_invariant_violated",
degradedFields: ["total", "hasMore"], and droppedRows. Treat total and
hasMore as untrusted for that incident response; normal-path pagination is
unchanged.
Direct REST access with an Agent API Key requires signals:read.
8) Run a historical backtest
Free Agent API Keys can run the fixed demo configuration with signals:read;
the returned trade list is capped at 5 rows. Pro and Elite keys can customize
the configuration within their tier's window limits.
curl -X POST "https://polytrackers.com/api/backtest" \
-H "Authorization: Bearer $AGENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source":{"kind":"anomalies","minSeverity":"HIGH"},
"stake":{"mode":"flat","amountUsd":100},
"startingBankrollUsd":1000,
"windowDays":30,
"slippageBps":50,
"feeBps":0
}'9) Readiness-gated real trade execution
Session auth requires manual trading readiness: trading wallet/signing setup, wallet funding, wallet risk config, circuit-breaker state, idempotency, reservations, region certification, and audit logging.
Session auth:
curl -X POST "https://polytrackers.com/api/trade/execute" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"conditionId":"0xconditionid",
"side":"YES",
"amount":20,
"limitPrice":0.5,
"idempotencyKey":"agent-trade-2026-04-16-001",
"regionCertification":true
}'Agent API Key with trade:execute scope:
curl -X POST "https://polytrackers.com/api/trade/execute" \
-H "Authorization: Bearer $AGENT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7c2b7f9e-47b8-4ad2-95b5-8f8d5f2f6a3c" \
-d '{"conditionId":"0x...","side":"YES","amount":20,"regionCertification":true}'CDP delegated-wallet browser users must submit explicit limit orders until the SDK adapter supports cap-priced immediate-or-cancel market orders. Non-CDP market buys retain the server-side slippage-cap path.
10) Cancel an own resting real order
Session callers can cancel their own resting order even when their tier cannot list all CLOB orders:
curl -X DELETE "https://polytrackers.com/api/trade/orders/$ORDER_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN"The two programmatic paths have different gates: direct REST bearer calls to
GET /api/trade/history, GET /api/trade/ledger,
GET /api/trade/money-summary, and
GET /api/trade/orders* require agent:full. MCP
pt_trade_history_get requires signals:read at every tier, while
pt_trade_orders_list and pt_trade_orders_get require signals:read plus
the Elite tier.
Agent API Key lifecycle
Generate Agent API Key (JWT bearer only)
Free callers can generate read-only Agent API Keys for MCP access; requested scopes are reduced to signals:read and MCP usage is capped at 200 requests/day. Pro callers may request signals:read, the narrow trade:execute scope, and the narrow agent:scan scope; Elite callers may additionally request agent:full. Accounts keep 1 active Agent API Key; rotate by revoking the old key, then generating a replacement and updating clients and agents (409 API_KEY_LIMIT_REACHED when the cap is reached).
curl -X POST "https://polytrackers.com/api/keys/generate" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"nickname":"Agent API Key","expiresInDays":90,"scopes":["signals:read"]}'expiresInDays: 1–365, defaults to 90.scopes: defaults to["signals:read"]. Free keys are always reduced tosignals:read; paid-tier validation still applies outside the free MCP/read surface. Addtrade:executeonly when intentionally creating a real-trading key. Pro-tier requests foragent:fullare stripped, whiletrade:executeandagent:scanare allowed where their automation surfaces are enabled.- The raw
api_keyis returned once in the response — store it securely.
List keys
curl "https://polytrackers.com/api/keys" \
-H "Authorization: Bearer $ACCESS_TOKEN"Use Agent API Key for public whale feed (Elite)
curl "https://polytrackers.com/api/public/whale-signals?limit=25" \
-H "Authorization: Bearer $AGENT_API_KEY"Outbound webhooks (Pro/Elite)
Create, list, delete, and test delivery endpoints live under /api/webhooks*:
GET /api/webhooks— list your registrations.POST /api/webhooks— register a new URL with optional filters (minScore,severity[],anomalyTypes[],marketCategories[]) and a secret. Capped at 5 (Pro) / 50 (Elite); over-limit returnsWEBHOOK_LIMIT_REACHED.DELETE /api/webhooks/{id}— soft-delete a registration. The row is retained for audit (withdeleted_atstamped andstatus=paused), it will stop receiving future deliveries, and historicaldelivery_logentries remain queryable.GET /api/webhooks/{id}/deliveries— recent delivery logs.POST /api/webhooks/{id}/test— fire a test payload.
Webhook delivery contract
Matching anomaly alerts are delivered as anomaly.detected JSON:
{
"event": "anomaly.detected",
"timestamp": "2026-07-20T14:30:00.000Z",
"anomaly": {
"id": "anomaly-id",
"score": 82,
"market_title": "Will BTC reach $150k in 2026?",
"market_slug": "will-btc-reach-150k-in-2026",
"market_url": "https://polymarket.com/event/will-btc-reach-150k-in-2026",
"current_price": 0.64,
"anomaly_type": "WHALE_ANOMALY",
"severity": "HIGH",
"side": "BUY",
"outcome": "YES",
"bet_recommendation": {
"recommendedBet": 100
},
"whale_flag": true
}
}- Delivery targets must be public HTTPS URLs. Redirects are treated as failures and are not followed.
- Any
2xxresponse completes delivery. A failed initial attempt is retried after 30 seconds, then 5 minutes, then 30 minutes: 4 total delivery attempts. - Each registration is capped at 100 delivery attempts per rolling hour, including retries.
- When the registration has a secret, verify
X-Webhook-Signatureagainstsha256=<HMAC_SHA256(raw_body, secret)>before parsing the JSON.
X-Webhook-Signature signs outbound user deliveries. It is distinct from the
inbound x-alchemy-signature required by /api/whale/webhook.
Mock-wallet unrealized P&L freshness
GET /api/wallets exposes mock-wallet unrealizedPnl as a persisted
point-in-time snapshot, not a continuously priced equity value.
unrealizedPnlMarkedAt records the pricing-pass timestamp; null means no
trustworthy mark timestamp exists. The companion unrealizedPnlIsSnapshot,
unrealizedPnlCaveat, and unrealizedPnlAuthority fields keep this contract
machine-readable.
For current equity decisions, use
pt_mock_analytics_get.wallet_mark_to_market.preferred_unrealized_pnl and
inspect live_pricing_status. A complete analytics pricing pass refreshes the
persisted snapshot after the read returns. Partial or unavailable pricing never
overwrites the last complete mark.
The reserved real-money-copy row is never accepted by mock analytics and
never receives its write-behind. In normalized /api/wallets and
GET|POST /api/trade/real-wallet responses, its mark timestamp is null and its
provenance instead points to real_trades for position P&L and
/api/trade/wallet-funds for spendable funds.
GET /api/wallets derives current-day realized P&L from
get_wallet_daily_copy_stats. dailyPnl and the backward-compatible
daily_pnl alias always carry the same UTC-day value. If the projection fails,
both are null and dailyPnlDegraded:true / dailyPnlStatus:"degraded" make
the partial response explicit; the route never substitutes the denormalized
mock-wallet counter. Reserved real-wallet legacy performance aliases are
projected from real_trades. A reserved real wallet whose risk configuration
is missing or does not select the real lane makes the daily projection fail
closed instead of reading mock_trades.
GET /api/trade/wallet-funds also returns equityIdentity for the authenticated
real wallet. Its complete identity is:
spendablePusd + openMarkedValue + pendingRedemptionValue - netDeposits = lifetimePnl
pendingRedemptionValue comes from the bounded Polymarket Data API positions
read and includes only positions marked redeemable. The separate
modeledPendingRedemptionValue is the unresolved-payout-row projection for
operations diagnostics only: it never enters lifetimePnl, and its absence
alone does not degrade the identity. netDeposits is the signed sum of deposit
and withdrawal ledger entries, while openMarkedValue excludes redeemable
venue positions so pending winnings are not double-counted. If the positions
read or another required component is unavailable, status: "degraded" and
lifetimePnl: null fail closed; callers must not reconstruct or display a
partial lifetime P&L as complete.
Deletion & audit retention
PolyTrackers favors soft deletion for user-owned resources so that reporting, billing, copy-trading analytics, and security audits remain reliable. The behavior per-resource is:
| Resource | Endpoint(s) | Behavior |
|---|---|---|
| Agent API Keys | DELETE /api/keys?id=… | Soft-revoke: is_active=false, revoked_at=now(). Authentication is rejected immediately; row is retained for audit. |
| Mock wallets | DELETE /api/wallets, DELETE /api/mock/wallet/{id} | Soft-delete: is_active=false, deleted_at=now(). Whale assignments are detached (active=false, removed_at=now()). mock_trades, whale_activity, and whale_decision_traces are preserved. Wallet names can be reused. |
| Outbound webhook registrations | DELETE /api/webhooks/{id} | Soft-delete: deleted_at=now(), status=paused. No future deliveries fire; historical delivery_log remains queryable via GET /api/webhooks/{id}/deliveries. |
| Alert preferences | DELETE /api/alerts/preferences | Hard reset of the preferences row. |
| Roster (whale) assignments | DELETE /api/roster/{walletId} | Detach: row retained, active=false, removed_at=now(). |
| Trade orders | DELETE /api/trade/orders/{id} | Cancels the live order at the CLOB; hard-removes the queued row. |
Mock trades are not user-deletable by design once placed — open, closed, and
resolved rows feed the leaderboard, copy-trading scoring, and tier-history
analytics. To exit an open mock position, close it through
POST /api/mock/resolve; this records the terminal state and preserves the
trade row for history.
Whale service + inbound webhooks
Existing whale-service endpoints remain supported and documented in OpenAPI:
- Wallet/roster management (
/api/wallets,/api/roster*) - Service status/config/control (
/api/whale/*) - Webhook ingest/events/replay (
/api/whale/webhook*)
For replay automation, use dryRun: true first, then replay live once validated.
Discovery endpoints
Anonymous, CDN-cached, and safe to fetch before you have a key. Every one of them describes a surface documented in this file:
| Path | Media type | What it is |
|---|---|---|
/openapi.json | application/json | The OpenAPI 3.1 contract for the REST API. /api/openapi.json is a 301 alias. |
/.well-known/api-catalog | application/linkset+json | RFC 9727 API catalog: a linkset naming the REST, MCP, and documentation MCP entry points, each with its service-desc (machine-readable description), service-doc (human documentation), and service-meta (metadata) links. The one URL to fetch when you want the developer resources and nothing else. |
/.well-known/mcp | application/json | MCP handshake manifest (alias /.well-known/mcp.json). |
/llms.txt | text/plain | Agent instructions: when to use PolyTrackers, automation-safe lanes, error shapes, and the crawl policy. |
/skill.md | text/markdown | The official agent skill — tool catalog, scopes, and workflows. |
MCP server
PolyTrackers exposes a stateless MCP endpoint for agent clients:
GET /api/mcp— unauthenticated health/capability probe.GET /.well-known/mcp(aliasGET /.well-known/mcp.json) — unauthenticated manifest describing this server: transport URL, advertised protocol versions, capabilities, auth scheme, and the stdio bridge package. Generated from the same constants the server itself uses, so it cannot drift.POST /api/mcp— JSON-RPC 2.0 over Streamable HTTP. SendAuthorization: Bearer <agent-api-key>— use the sameptk_...key as your REST/API examples.GET /api/mcp/events— optional authenticated SSE notifications foranomalies,whale_signals,copy_signals, andscan_progress, with: keepaliveevery 15s. SendAuthorization: Bearer ptk_.... Thewhale_signalsandcopy_signalstopics are Elite-only; requests for them from Free or Pro keys are silently dropped while the200stream remains live for permitted topics.
SSE connections last at most 800 seconds (~13 minutes), so reconnect when the
server closes the stream. There is no Last-Event-ID resumption: each reconnect
starts at the current Redis position ($), so events published while
disconnected are permanently missed.
When the client supports Streamable HTTP, point it at
https://polytrackers.com/api/mcp with Authorization: Bearer <agent-api-key>.
For stdio-only MCP hosts, run the published bridge with
npx -y @polytrackers/mcp-stdio; it relays stdio JSON-RPC to that endpoint. It
reads POLYTRACKERS_API_KEY (required),
POLYTRACKERS_MCP_URL (optional, defaults to the endpoint above),
POLYTRACKERS_MCP_ALLOWED_HOSTS (optional host allowlist), and
POLYTRACKERS_MCP_TIMEOUT_MS (optional). Full hosted and stdio setup examples
are published in /skill.md.
To test strategies programmatically with no real money — the mock (paper) trading tool flow, free-tier reads, and going-live fees — see the Polymarket Mock Trading API guide.
Agent skill distribution
Install the PolyTrackers agent skill (which bundles this MCP setup) with one
command: npx skills add polytrackers/polymarket-copy-trading-skill. Official
distribution channels:
| Channel | Location |
|---|---|
| GitHub | polytrackers/polymarket-copy-trading-skill |
| skills.sh | polytrackers/polymarket-copy-trading-skill |
| npm | @polytrackers/mcp-stdio (stdio bridge) |
Scopes:
signals:read— read-only tool/resource catalog for Free+; free keys are shaped by 7-day base anomaly history (up to 28 days with activated referral rewards), top-5 delayed anomalies, and the 200 requests/day MCP quota.trade:execute— permitspt_trade_executefor Pro+ afterpt_trade_preflightapproval.agent:scan— permitspt_scan_triggerfor Pro+ without granting broader writes.agent:full— current Elite write/execution superset.
Versioning & changes
The current REST/OpenAPI contract version is 2.0.0, sourced from the
canonical OpenAPI document and served at /openapi.json. The hosted MCP server
version is 0.1.0, sourced from the server package version and returned by
the MCP initialize response and each tool response's _meta.server_version.
Additive endpoints and tools may ship without a version bump.
Version header
The REST contract is versioned in a response header, not in the URL path.
Every /api/* response carries:
| Header | Meaning |
|---|---|
API-Version | Contract version that produced this response, e.g. 2.0.0. Matches info.version in the OpenAPI document. |
Assert on it at startup and fail loudly on an unexpected major, rather than
discovering a change through a parse error later. The header is response-only:
no request header changes how a route behaves, so there is nothing here that
can be pinned to and later withdrawn. Paths outside the proxy's matcher
(/api/cron/*, /api/whale/webhook, /api/goldsky/shadow) are
machine-to-machine and omit it.
The OpenAPI document declares this header on every response it describes, as
$ref: "#/components/headers/ApiVersion" — so a generated client sees it
without reading this page, and a spec parser can detect the versioning
strategy. The four POST /api/whale/webhook responses deliberately carry no
such declaration, because that path is outside the matcher. A contract test
holds both halves in place against the matcher in proxy.ts.
How the number moves:
- Patch/minor — additive only. New endpoints, new optional request fields, and new response fields ship without a major bump, so clients must ignore response fields they do not recognise.
- Major — anything that could break a correct client: a removed or renamed field, a narrowed type, a changed status code, or a retired endpoint. A major is always preceded by the deprecation signal below.
Deprecation and sunset headers
A retired endpoint announces itself on the wire, not only in this document:
| Header | Meaning |
|---|---|
Deprecation | RFC 9745 structured-field date — @<epoch seconds> — for when the deprecation was announced. |
Sunset | RFC 8594 HTTP-date for when the endpoint stops serving. A date in the past means it is already gone. |
Link | rel="successor-version" pointing at the replacement, plus rel="deprecation" pointing at this page. |
POST /api/whale/trade is the current example: retired on 2026-02-27, it
answers 410 Gone and carries all three headers pointing at
POST /api/whale/webhook. Poll Deprecation on the endpoints you depend on —
it is the earliest machine-readable warning available, and it is set long
before the Sunset date arrives.
Compatibility and deprecation policy:
- Breaking REST contract changes are recorded in this section when they ship.
- MCP tool additions, removals, and renames are recorded in the
polytrackers://changelogresource. Its current catalog count and fingerprint are checked against the registered catalog in CI. - Breaking MCP changes bump the server minor version. A renamed tool remains
available for one minor version with
deprecated: trueand a replacement in its description before removal. - The MCP HTTP transport does not emit a
Deprecationresponse header for tool changes; the REST headers above cover REST endpoints only. For MCP, consult this section andpolytrackers://changelog.
Change log:
-
2026-08-23: The OpenAPI document now declares
API-Versionon every response it describes, instead of only defining the header component and describing it in prose. Documentation only — the header itself has shipped on the wire since earlier the same day, and no endpoint's status, body, or media type changed. -
2026-08-23: Every
/api/*response now carriesAPI-Version, and the retiredPOST /api/whale/tradeadditionally carriesDeprecation,Sunset, and a successorLink. Additive headers only — no status code, body, or cache directive changed, and page routes are untouched. Every OpenAPI operation also now has a description; a contract test fails the build if one ships without. -
2026-08-14: Wallet copy pause is now a three-state REST/MCP contract:
copying,buys_paused, orfully_paused. Buys-only mode blocks new BUY mirroring withWALLET_COPY_BUYS_PAUSEDwhile paper and real SELL exits keep flowing. The v2 MCP setter prefersmodeand retains legacycopyPaused:true|false; every live transition remains atomically audited, with explicit confirmation and a best-effort security email for real wallets. -
2026-08-14: Added the operator-allowlisted, Elite, read-only, MCP-only
pt_copy_replication_aggregates_getinternal evidence tool over the service-role Copy Receipt V1 aggregate RPC. It requires one exact whale, fixes the RPC limit at one, and adds index-backed lookup plus a caller-side five-second PostgREST request deadline. Database-owned 20/40-condition confidence floors and below-floor null metrics are unchanged; ordinary customer keys fail closed and no REST or browser surface was added. -
2026-08-18: Paper and dedicated real-copy wallets now expose
fixedStakeUsd. Zero preserves conviction/percentage sizing; a positive value of at least $1 requests the same USD amount for every copied BUY and bypasses the copy multiplier. Kelly, position, max-bet, balance, authorization, breaker, reservation, and venue gates remain authoritative. The dashboard plus both guarded MCP config writers can read, preview, and update the wallet-scoped field; existing wallets default to off. -
2026-08-11: Added the free read-only MCP tool
pt_whale_forward_returns_getfor bounded forward-return counterfactuals across arbitrary wallet cohorts. It preserves one row per request entry, enforces exact decision/cutoff windows, and exposes unresolved, truncation, fee, resolution-source, and vendor-call-budget evidence. -
2026-08-10: Dedicated real-copy wallets now persist a guarded 0–10%
realCopySlippageTolerance, exposed through the real-wallet MCP config tools. Where the venue ceiling permits, BUY FAK caps retain at least one market tick of headroom above the observed ask, and deterministic signed-cap rejections are no longer replayed through Goldsky promotion recovery. -
2026-08-10: Recommendation and digest-suggestion rows now expose display-name source/time plus Polymarket's stable
pseudonym. Legacy aliases beginning with a wallet address no longer override the current leaderboard name, and compact/byte-capped/cursor-replayed shapes retain the provenance. -
2026-08-07: Rollout-eligible dedicated real-copy wallets can now set a 1–2x owner-only sizing multiplier through the authenticated dashboard. The server gate fails closed to 1x, all existing caps remain authoritative, and MCP real-wallet writes continue to exclude the field.
-
2026-08-10: Real-wallet dashboard and Elite MCP config surfaces now read, preview, and write authorization-scoped
minPerTrade. Null/0 disables the floor; positive values are bounded to $1 through the signed copy-trading authorization'smaxPerTrade, restore only percentage-reduced dust, and never exceed the strategy-sized cost. The floor applies only whenfixedStakeUsdis off and is explicitly distinct from risk-rulemaxPerTradeandminTradeSize. -
2026-08-07: Session-authenticated
PUT /api/whale/confignow routes real-wallet risk changes through the atomic config-plus-audit RPC and sends the owner security email. Real-wallet requests rejectexecutionMode; paper-wallet behavior is unchanged. -
2026-08-07: Whale-edge loads are now batch-independent under deterministic per-wallet caps.
coverageTruncatedis wallet-specific, SPLIT/non-directional evidence is exposed throughnonDirectionalSampleCount*, androi_provenanceincludes the capital used by each ROI window. -
2026-08-07: Leaderboard, recommendation, and digest-suggestion rows now share canonical ROI/sample projections and a
roi_provenanceblock across full, compact, and replayed snapshot shapes. BaresampleCountmeans the directional edge-row count; recommendation admission evidence is separately named. Explicit edge-load fallback uses labeled 365-day receipts-only counts with null ROI. -
2026-08-04:
pt_recommendations_getnow returns snapshot-pinned opaque continuation cursors plus explicit total/returned/has-more counts. Continue with the same projection and settled-sample filter when the byte cap trims a page;min_settled_samplesnarrows the population and is not continuation. -
2026-08-03: Added Elite
agent:fullpt_real_wallet_copy_config_get/pt_real_wallet_copy_config_updateandpt_real_wallet_breaker_resume/pt_real_wallet_breaker_kill. Every real-money preview or write requiresconfirm_real_money:true; live changes append an audit event and send a best-effort security email. The strict config writer excludesexecutionModeandcopySizingMultiplier. -
2026-07-31: Added Elite
agent:fullpt_mock_wallet_copy_config_get/pt_mock_wallet_copy_config_updatefor full caller-owned paper-wallet risk reads and guarded dry-run/live updates. The strict write schema excludesexecutionMode, real-wallet rows are unreachable, and zero-value warnings are returned with the preview. -
2026-07-26: Added
GET /api/trade/money-summary(user-scoped real-money totals/counts aggregate; session oragent:full, same auth semantics asGET /api/trade/ledger). -
2026-07-21: Added the paper-only wallet copy-sizing multiplier to the wallet risk-config contract and
pt_mock_wallet_copy_sizing_update; the MCP catalog baseline was then 65 tools and real-copy sizing was 1x. The 2026-08-07 owner-dashboard 1–2x rollout supersedes that historical behavior. -
2026-07-20: Published the version/deprecation policy and recorded the 2.0.0 OpenAPI contract plus the then-current 64-tool MCP catalog baseline.
Reliability notes
- Handle
429on all rate-limited endpoints and respectRetry-After. - Treat trade execution as idempotent when you provide
idempotencyKey(idempotent replays return200withidempotencyReused: true). POST /api/keys/generatereturns the raw key once — store it securely. Each account keeps one active Agent API Key; rotate by revoking the old key, then generating a replacement and updating clients/agents./api/anomalies/{id}may return404when a record falls outside your tier window.TIER_UPGRADE_REQUIREDresponses includerequiredTier,currentTier, andupgradeUrl— surface these to the user/agent verbatim.