{"openapi":"3.1.0","info":{"title":"Bonker Public API","version":"1.0.0","description":"Machine-readable contract for bonker.wtf public JSON API routes."},"servers":[{"url":"https://bonker.wtf","description":"Production"},{"url":"http://localhost:3456","description":"Local Express server"}],"externalDocs":{"description":"Human docs","url":"https://bonker.wtf/docs/api"},"tags":[{"name":"Spec"},{"name":"Tokens"},{"name":"Presales"},{"name":"Discovery"},{"name":"Genesis"},{"name":"Creator"},{"name":"System"}],"paths":{"/api/spec":{"get":{"tags":["Spec"],"summary":"OpenAPI specification","description":"Returns this OpenAPI document. Uses the global API rate limit.","responses":{"200":{"description":"OpenAPI document","content":{"application/json":{"schema":{"type":"object"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tokens":{"get":{"tags":["Tokens"],"summary":"List indexed token launches","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"search","in":"query","schema":{"type":"string","maxLength":100}},{"name":"sort","in":"query","schema":{"type":"string","enum":["newest","oldest","mcap","volume","trending"],"default":"newest"}},{"name":"creator","in":"query","schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"Paginated token list","content":{"application/json":{"schema":{"type":"object","required":["tokens","total","page","pages"],"properties":{"tokens":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"name":{"type":"string"},"symbol":{"type":"string"},"image":{"type":["string","null"]},"description":{"type":["string","null"]},"deployer":{"$ref":"#/components/schemas/EvmAddress"},"mcap":{"type":["number","null"]},"volume24h":{"type":["number","null"]},"priceChange24h":{"type":["number","null"]},"createdAt":{"type":["string","null"]}}}},"total":{"type":"integer"},"page":{"type":"integer"},"pages":{"type":"integer"}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tokens/{address}":{"get":{"tags":["Tokens"],"summary":"Read one indexed token","description":"RPC-heavy route with on-chain feature enrichment. Limit: 30 requests per minute per IP, after the global /api limit.","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"Token detail","headers":{"RateLimit-Limit":{"description":"Per-minute limit for this detail route.","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"allOf":[{"type":"object","additionalProperties":true,"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"name":{"type":"string"},"symbol":{"type":"string"},"image":{"type":["string","null"]},"description":{"type":["string","null"]},"deployer":{"$ref":"#/components/schemas/EvmAddress"},"mcap":{"type":["number","null"]},"volume24h":{"type":["number","null"]},"priceChange24h":{"type":["number","null"]},"createdAt":{"type":["string","null"]}}},{"type":"object","properties":{"features":{"type":["object","null"],"additionalProperties":true}}}]}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["Tokens"],"summary":"Update token display metadata with a wallet signature","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["timestamp","signature"],"properties":{"description":{"type":"string","maxLength":500},"image":{"type":"string","maxLength":500},"website":{"type":"string","maxLength":300},"twitter":{"type":"string","maxLength":200},"telegram":{"type":"string","maxLength":200},"farcaster":{"type":"string","maxLength":200},"timestamp":{"type":["integer","string"]},"signature":{"type":"string","pattern":"^0x[a-fA-F0-9]+$"}}}}}},"responses":{"200":{"description":"Updated token detail","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"object","additionalProperties":true,"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"name":{"type":"string"},"symbol":{"type":"string"},"image":{"type":["string","null"]},"description":{"type":["string","null"]},"deployer":{"$ref":"#/components/schemas/EvmAddress"},"mcap":{"type":["number","null"]},"volume24h":{"type":["number","null"]},"priceChange24h":{"type":["number","null"]},"createdAt":{"type":["string","null"]}}}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tokens/{address}/holders":{"get":{"tags":["Tokens"],"summary":"Read cached top holders for a token","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Holder list or graceful unavailable response","content":{"application/json":{"schema":{"type":"object","required":["holders","unavailable"],"properties":{"holders":{"type":"array","items":{"type":"object","properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"balance":{"type":"string"},"percent":{"type":"number"},"label":{"type":["string","null"]},"isContract":{"type":"boolean"}}}},"unavailable":{"type":"boolean"}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tokens/{address}/trades":{"get":{"tags":["Tokens"],"summary":"Read recent indexed swaps for a token","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"before","in":"query","schema":{"type":"string","pattern":"^\\d+:\\d+$"}}],"responses":{"200":{"description":"Trade page","content":{"application/json":{"schema":{"type":"object","required":["trades","nextBefore"],"properties":{"trades":{"type":"array","items":{"type":"object","additionalProperties":true}},"nextBefore":{"type":["string","null"]}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tokens/{address}/comments":{"get":{"tags":["Tokens"],"summary":"List comments for a token","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Comment page","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Tokens"],"summary":"Create a wallet-signed token comment or reply","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["author","body","timestamp","nonce","signature"],"properties":{"author":{"$ref":"#/components/schemas/EvmAddress"},"body":{"type":"string","maxLength":500},"parentId":{"type":["integer","null"]},"timestamp":{"type":["integer","string"]},"nonce":{"type":"string","pattern":"^0x[a-fA-F0-9]{32,128}$"},"signature":{"type":"string","pattern":"^0x[a-fA-F0-9]+$"}}}}}},"responses":{"201":{"description":"Created comment","content":{"application/json":{"schema":{"type":"object","properties":{"comment":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tokens/{address}/refresh":{"post":{"tags":["Tokens"],"summary":"Invalidate one token detail enrichment cache entry","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"204":{"description":"Cache invalidated"},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tokens/{address}/sync-metadata":{"post":{"tags":["Tokens"],"summary":"Mirror the token’s on-chain image/metadata into the DB","description":"Reads the token’s on-chain image and metadata pointers and copies them into the indexed row the site renders from. Permissionless: it can only ever set the DB to match on-chain truth.","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"Refreshed token","content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"object","additionalProperties":true,"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"name":{"type":"string"},"symbol":{"type":"string"},"image":{"type":["string","null"]},"description":{"type":["string","null"]},"deployer":{"$ref":"#/components/schemas/EvmAddress"},"mcap":{"type":["number","null"]},"volume24h":{"type":["number","null"]},"priceChange24h":{"type":["number","null"]},"createdAt":{"type":["string","null"]}}}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/presales":{"get":{"tags":["Presales"],"summary":"List presales","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["NotCreated","Active","SuccessfulMinimumHit","SuccessfulMaximumHit","Failed","Claimable"]}}],"responses":{"200":{"description":"Presale list","content":{"application/json":{"schema":{"type":"object","required":["presales","contract"],"properties":{"presales":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"integer"},"status":{"type":"string"},"tokenName":{"type":"string"},"tokenSymbol":{"type":"string"},"tokenImage":{"type":["string","null"]},"minEthGoal":{"type":["string","null"]},"maxEthGoal":{"type":["string","null"]},"ethRaised":{"type":["string","null"]}}}},"contract":{"type":["string","null"]}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/presales/{id}":{"get":{"tags":["Presales"],"summary":"Read one presale","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Presale detail","content":{"application/json":{"schema":{"type":"object","properties":{"presale":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"integer"},"status":{"type":"string"},"tokenName":{"type":"string"},"tokenSymbol":{"type":"string"},"tokenImage":{"type":["string","null"]},"minEthGoal":{"type":["string","null"]},"maxEthGoal":{"type":["string","null"]},"ethRaised":{"type":["string","null"]}}},"contract":{"type":["string","null"]}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/random-token":{"get":{"tags":["Discovery"],"summary":"Get a launch-form token suggestion","responses":{"200":{"description":"Random token template","content":{"application/json":{"schema":{"type":"object","required":["name","symbol","description"],"properties":{"name":{"type":"string"},"symbol":{"type":"string"},"description":{"type":"string"}}}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/events":{"post":{"tags":["Discovery"],"summary":"Record an anonymous launch funnel event","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["event"],"properties":{"event":{"type":"string","enum":["launch_form_viewed","wallet_connected","tx_submitted","tx_confirmed","tx_failed"]},"address":{"$ref":"#/components/schemas/EvmAddress"},"wallet":{"$ref":"#/components/schemas/EvmAddress"}}}}}},"responses":{"201":{"description":"Recorded event","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/upload-image":{"post":{"tags":["Discovery"],"summary":"Store a launch-form image and return its public URL","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["image"],"properties":{"image":{"type":"string","format":"binary","description":"JPEG, PNG, GIF, or WEBP, max 128KB."}}}}}},"responses":{"200":{"description":"Stored image URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageResult"}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/generate-image":{"post":{"tags":["Discovery"],"summary":"Generate a token image and return its public URL","description":"Route limit: 1 request per 10 seconds per IP.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","symbol","description"],"properties":{"name":{"type":"string","maxLength":50},"symbol":{"type":"string","maxLength":10},"description":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"Generated image URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageResult"}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/stats":{"get":{"tags":["Discovery"],"summary":"Read aggregate token stats","responses":{"200":{"description":"Aggregate stats","content":{"application/json":{"schema":{"type":"object","properties":{"tokensLaunched":{"type":"integer"},"totalMcap":{"type":"number"},"volume24h":{"type":"number"},"totalTransfers":{"type":"integer"}}}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/leaderboard":{"get":{"tags":["Discovery"],"summary":"Read creator leaderboard","parameters":[{"name":"sort","in":"query","schema":{"type":"string","enum":["volume","tokens"],"default":"volume"}}],"responses":{"200":{"description":"Creator leaderboard","content":{"application/json":{"schema":{"type":"object","properties":{"creators":{"type":"array","items":{"type":"object","additionalProperties":true}},"sort":{"type":"string"}}}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/stats":{"get":{"tags":["Genesis"],"summary":"Read genesis campaign counts","responses":{"200":{"description":"Genesis allowlist and qualification counts","content":{"application/json":{"schema":{"type":"object","required":["allowlistCount","qualifiedCount"],"properties":{"allowlistCount":{"type":"integer"},"qualifiedCount":{"type":"integer"}}}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/leaderboard":{"get":{"tags":["Genesis"],"summary":"Read genesis referral leaderboard","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Top genesis referrers by qualified invites","content":{"application/json":{"schema":{"type":"object","required":["leaders"],"properties":{"leaders":{"type":"array","items":{"type":"object","required":["referrer","invites"],"properties":{"referrer":{"$ref":"#/components/schemas/EvmAddress"},"invites":{"type":"integer"}}}}}}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/referrals/{address}":{"get":{"tags":["Genesis"],"summary":"Read one wallet genesis referral stats","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"Genesis referral stats","content":{"application/json":{"schema":{"type":"object","required":["address","invites","rank"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"invites":{"type":"integer"},"rank":{"type":["integer","null"]}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/status/{address}":{"get":{"tags":["Genesis"],"summary":"Read one wallet's live genesis allowlist status","description":"Live allowlist state (joined? qualified? referral count? has it launched a token?), straight from genesis_allowlist — distinct from GET /api/genesis/claim/{address}, which only answers once the operator ships the frozen airdrop manifest at genesis. onList:false for a wallet that never joined is a normal 200, not a 404.","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"Genesis allowlist status for the wallet","content":{"application/json":{"schema":{"type":"object","required":["onList"],"properties":{"onList":{"type":"boolean"},"address":{"$ref":"#/components/schemas/EvmAddress"},"qualified":{"type":["boolean","null"],"description":"null only for a grandfathered legacy row still pending the Blockscout poller; Farcaster/X joins qualify synchronously."},"source":{"type":"string","enum":["web","farcaster","x"]},"createdAt":{"type":"integer","description":"Unix seconds."},"qualifiedReferrals":{"type":"integer"},"launched":{"type":"boolean","description":"Whether this wallet has deployed at least one token through Bonker (the flat genesis launch bonus)."}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/claim/{address}":{"get":{"tags":["Genesis"],"summary":"Read one wallet genesis airdrop claim proof","description":"Reads the shipped frozen genesis airdrop manifest. Before the manifest ships, frozen is false and allocation/proof fields are null.","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"Genesis airdrop claim state for the wallet","content":{"application/json":{"schema":{"type":"object","required":["frozen","token","airdrop","eligible","allocated","proof","merkleRoot"],"properties":{"frozen":{"type":"boolean"},"token":{"anyOf":[{"$ref":"#/components/schemas/EvmAddress"},{"type":"null"}]},"airdrop":{"$ref":"#/components/schemas/EvmAddress"},"eligible":{"type":"boolean"},"allocated":{"type":["string","null"],"description":"Base-unit token allocation as a decimal string."},"proof":{"type":["array","null"],"items":{"type":"string","pattern":"^0x[a-fA-F0-9]{64}$"}},"merkleRoot":{"type":["string","null"],"pattern":"^0x[a-fA-F0-9]{64}$"}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/allowlist":{"post":{"tags":["Genesis"],"summary":"Retired anonymous Genesis signup (410)","description":"Retired route. Returns 410 Gone; the legacy request shape accepted xHandle or farcaster. Use the Farcaster cast-native join or the verified X quest routes instead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["address"],"anyOf":[{"required":["xHandle"]},{"required":["farcaster"]}],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"xHandle":{"type":"string","maxLength":30,"description":"X handle, sanitized to alphanumeric plus underscore and stored without a leading @."},"farcaster":{"type":"string","maxLength":40,"description":"Farcaster username, sanitized to letters, digits, underscore, dot, and hyphen."},"ref":{"type":"string"},"timestamp":{"type":["integer","string"],"description":"Only for refreshing an EXISTING entry: ms epoch, paired with `signature`, valid for 5 minutes."},"signature":{"type":"string","pattern":"^0x[a-fA-F0-9]+$","description":"Only for refreshing an EXISTING entry: wallet signature over \"Update genesis socials for {address} at {timestamp}\\n{JSON handles}\", recovered signer must equal `address`. First joins need no signature; without a valid owner signature an existing entry’s socials are never overwritten."},"website":{"type":"string","description":"Hidden honeypot field. Filled values are rejected."}}}}}},"responses":{"201":{"description":"Allowlist signup accepted","content":{"application/json":{"schema":{"type":"object","required":["ok","count","ref"],"properties":{"ok":{"type":"boolean"},"count":{"type":"integer"},"ref":{"$ref":"#/components/schemas/EvmAddress","description":"The joiner's own address — their shareable referral code, not the recorded referrer."}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/x/config":{"get":{"tags":["Genesis"],"summary":"Read X Genesis quest availability","responses":{"200":{"description":"Quest configuration status","content":{"application/json":{"schema":{"type":"object","required":["configured"],"properties":{"configured":{"type":"boolean"}}}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/x/oauth-url":{"get":{"tags":["Genesis"],"summary":"Start the X OAuth quest connection","responses":{"200":{"description":"X OAuth authorization URL","content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/x/callback":{"get":{"tags":["Genesis"],"summary":"Complete X OAuth PKCE callback","responses":{"200":{"description":"HTML popup callback that posts a short-lived identity token to the opener"},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/genesis/x/submit":{"post":{"tags":["Genesis"],"summary":"Submit a verified X Genesis quest","description":"Verifies OAuth identity, signed wallet, reply to the configured pinned tweet with two mentions, cached follow, account-quality gate, and optional qualified X referral.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["address","signature","timestamp","nonce","xIdentityToken","tweetUrl"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"signature":{"type":"string"},"timestamp":{"type":["integer","string"]},"nonce":{"type":"string"},"xIdentityToken":{"type":"string"},"tweetUrl":{"type":"string","format":"uri"},"xref":{"type":"string"}}}}}},"responses":{"200":{"description":"Quest result","content":{"application/json":{"schema":{"type":"object","required":["qualified"],"properties":{"qualified":{"type":"boolean"},"reason":{"type":"string"}}}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/referrals/{address}":{"get":{"tags":["Creator"],"summary":"Read referral stats for a wallet","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"Referral stats","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/portfolio/{address}":{"get":{"tags":["Creator"],"summary":"Read LP fee recipient portfolio for a wallet","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"Portfolio","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/creator/{address}/unclaimed":{"get":{"tags":["Creator"],"summary":"Read creator claimable WETH summary","parameters":[{"name":"address","in":"path","required":true,"schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"Claimable fee summary","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/health":{"get":{"tags":["System"],"summary":"Read server health and poller status","responses":{"200":{"description":"Healthy or degraded health response","content":{"application/json":{"schema":{"type":"object","required":["status","checks","pollers","uptime"],"properties":{"status":{"type":"string","enum":["ok","degraded","unhealthy"]},"checks":{"type":"object","additionalProperties":true,"properties":{"tokens":{"type":"object","additionalProperties":true},"presales":{"type":"object","additionalProperties":true},"enrichers":{"type":"object","additionalProperties":true}}},"pollers":{"type":"object","additionalProperties":true,"properties":{"tokens":{"type":"object","additionalProperties":true,"properties":{"lastPoll":{"type":"integer"},"lastBlock":{"type":"integer"},"enrichers":{"type":"object","additionalProperties":true,"properties":{"dexscreener":{"type":"object","additionalProperties":true},"blockscout":{"type":"object","additionalProperties":true}}}}}}},"presaleContract":{"type":["string","null"]},"uptime":{"type":"integer"}}}}}},"429":{"description":"JSON error response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Unhealthy required runtime dependency","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}}},"components":{"schemas":{"EvmAddress":{"type":"string","pattern":"^0x[a-fA-F0-9]{40}$","example":"0x1111111111111111111111111111111111111111"},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"ImageResult":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","example":"https://bonker.wtf/i/<sha256>.png"}}}}},"x-rateLimits":{"globalApi":{"appliesTo":"/api/*","windowSeconds":60,"maxRequests":100,"headers":"standard RateLimit headers"},"tokenDetail":{"appliesTo":"GET /api/tokens/{address}","windowSeconds":60,"maxRequests":30,"reason":"Token detail performs RPC-backed enrichment and is more expensive than list routes."},"imageGeneration":{"appliesTo":"POST /api/generate-image","windowSeconds":10,"maxRequests":1},"imageUpload":{"appliesTo":"POST /api/upload-image","windowSeconds":60,"maxRequests":30,"reason":"Each upload writes to disk; a dedicated cap bounds per-IP disk growth on top of the global /api limit."},"genesisSignup":{"appliesTo":"POST /api/genesis/allowlist","windowSeconds":10,"maxRequests":3},"comments":{"appliesTo":"POST /api/tokens/{address}/comments","windowSeconds":60,"maxRequests":10}}}