docs
THE DESK, END TO END
what every screen shows, how to read each column, and what every absent number means
written against the shipped code, not a plan
honest read
how the numbers stay honest
the rules the whole product follows, on every surface
- real data in · every production read uses a real source or a real computation over source data · no fixture row, no demo token, no fabricated wallet, KOL, caller, X post, view count, holder percentage or market cap · a fake-looking but plausible value is worse than an empty value
- the field data contract · every important field preserves { value, source, ts, status, reason } · value, the source it came from, its observation/update timestamp, its state, and the reason when it is unavailable · provenance is never hidden and a provider error is never turned into a normal value
- zero versus null versus missing · a source that reports 0 means ZERO and displays 0 · a field the source does not report is MISSING/NULL · a provider failure is UNAVAILABLE · they are not interchangeable
- the worked example · GMGN genuinely reports holder_count = 0, so the desk displays 0 · GMGN fails to return holder_count, so the desk shows the missing or error state with its reason and never displays 0
- absence always carries its real reason · key not configured, source rate limited, source blocked, source timed out, provider returned an error, the source did not report this field, no pair exists, insufficient data, or the source is stale · a visually empty value never hides the reason
- market cap is market cap · never called token price, unit price, per-token value or generated valuation, and a fake market-cap number is never created to fill a UI slot
- a source failure shows the source's own failure state · it is only replaced by another source when the implementation performs a real fallback, and that fallback is itself a real source
market
the market screener
/explore · the live market discovery surface · separate lists, never mixed
/explore is the live market discovery surface. each control on the tab row loads its own real list, and the source outputs are never mixed into one · a source that failed renders its own reason instead of a substitute list
| tab | what it loads |
|---|---|
| Trending | the source's own trending board · GET /api/social/trending |
| Hot (GMGN) | the GMGN market/trending read · GET /api/social/hot · the tab reads the 24h window, and the endpoint accepts only 1h, 6h or 24h |
| Call Out | the tracked-holder/callout surface · GET /api/social/callout · share = tracked wallets ÷ total holders, eligible above the threshold · the displayed threshold is read from the payload, never hardcoded |
| Search + filters | narrows whichever list is loaded · query by ticker, name or ca, plus min market cap, min liquidity, min 24h volume and min holders |
a search box narrows the currently loaded list · typing a ticker, a name or a ca filters it, and a ca is detected and offered as a direct link to the token page. the filter row adds a sort picker and four minimums · market cap, liquidity, 24h volume and holders. a positive bound excludes a row whose value the source left missing, and the desk states how many rows were excluded for a missing bounded value. the max-age field is disabled on purpose: neither source reports a token's age, so the bound cannot be honoured
below the filter row the status line states the list's own freshness and a refresh control. the Call Out tab also prints its live threshold, read from the payload, never hardcoded
the seven columns
| column | what it shows | when it reads a dash |
|---|---|---|
| # | the row's position in the list as currently shown · the default sort is source order, and any filter, sort or mode change re-ranks from one | a row the source did not admit is not drawn at all |
| token | the token logo, the ticker and the project name · the row opens /coin/<mint>, and the mint is a real on-chain token id | a name the source did not report prints as unnamed |
| mc | the token's market capitalisation in USD, not its unit price | no market cap reported by this list · price is never substituted for market cap |
| 1h | the 1h movement the source reported, or the valid calculation the implementation uses | no 1h change reported · never a fabricated percentage |
| 24h | the 24h movement the source reported, or the valid calculation the implementation uses | no 24h change reported · never a fabricated percentage |
| kol | the tracked or verified buyer signal · only the Call Out list carries buyers, so Trending and Hot carry no buyer data · a holder is not a buyer and a buyer is not a KOL: a wallet is a buyer only where the source supports that classification, and a KOL only where the tracking system identifies it | this list carries no buyer data |
| ai | the decision model's own 0-100 score over the market fields the row carries · it is not a price prediction, a guarantee, proof of future performance, or a replacement for source data, and the model uses only backend-supplied context | no scorable market field on this list, or the model withheld the score because a required input is missing |
the ai badge is the decision model's own score over the market fields the row carries. it grades green at 70 and above, amber from 45 to 69, and red below 45. a small star on the badge marks a partial read: the list carries only part of the model's weight, and the tooltip states exactly how much. the token page reads the rest
token
one token, read deep
/coin/<mint> · the dashboard for a single mint
the page is the header, the AI decision rail, a section tab strip, a 2x2 card grid and a full-width market card. market cap is shown once, in the header; no other panel repeats it, and no unit-per-token figure appears anywhere on the page
- header strip
- market cap · liquidity · 24h volume · holders · age
- section tabs
- Smart Money · Holders · X Radar · Market Data · About
the ai decision panel
the decision rail is a gauge with the score out of 100, a filled verdict banner, a confidence bar, the positive and negative signals, the exact inputs that are missing, a verified · inferred · unknown split, and a factor breakdown that shows each component's weight and the points it earned
| score | verdict | band text |
|---|---|---|
| 80-100 | BUY SETUP | strong setup |
| 65-79 | WATCH | watch closely |
| 45-64 | WATCH | high uncertainty |
| 0-44 | AVOID | avoid |
| a critical input absent | INSUFFICIENT DATA | the score is withheld |
holder concentration
the main metric is the TOP 10 WALLETS / ACCOUNTS COMBINED SHARE · the labels are explicit and never the ambiguous Top 10%. where available the panel also shows the largest wallet share, the holder count, the individual top accounts, and the burn, LP, known and unidentified categories. when the source provided only the aggregate percentage the panel says so instead of inventing a breakdown, and it does not claim ultimate-owner resolution unless the implementation resolves token-account ownership. an unread read prints unread, never 0
X Radar
X Radar lists up to five qualifying posts about the token, each already past the source's minimum-view and valid-link gates, with the author, the post age, the view count and an open link. NO QUALIFYING POSTS right now is a distinct state from X RADAR UNAVAILABLE right now
Smart Money and the tracked-holder ratio
the two cards beside the holders panel name recognised buyers who provably bought · a hold is never reported as a buy and an unknown wallet is never labelled a KOL · and the tracked-holder ratio, which is the tracked holders over the total holders. being in the holders list does not make a wallet a KOL, and being tracked does not mean it bought this token. when the tracked read is capped the share is marked as a lower bound, and a partial read says so
mind
mind · the workspace
/mind and /mind/<token> · projects, chats and a grounded read
- 1projects
- 2chats
- 3paste a ca
- 4grounded read
projects, not chats. the sidebar creates, renames and deletes projects; opening a project expands its chats beneath it with a single new-chat action. deleting a project or a chat names exactly what it takes with it and asks first. projects and chats are stored server-side, and a chat with no project lands under the default one, so a thread is never orphaned
the conversation is one column with the composer fixed at the bottom. the composer accepts normal questions and a pasted token ca/mint. when a ca is supplied, mind identifies the token and the thread receives the token context · token identity, market data, intelligence, holders, security and X/news intelligence · and follow-ups continue on that established context: PASTE CA ONCE · BUILD GROUNDED CONTEXT · ASK FOLLOW-UP QUESTIONS · KEEP THE TOKEN CONTEXT. the ca does not have to be pasted into every message. switching the ca opens a new chat, so no read bleeds between tokens
the grounded read
the ground read behind a grounded answer surfaces analyze, holders, security, news, x-intel and tools · only the fields the backend actually returns
| surface | what it covers |
|---|---|
| analyze | the actual market/token signals the backend exposes: market pair, liquidity, turnover, pressure, top holders, dev signals, authorities, age, momentum, and the organic/verified signals · only fields actually returned |
| holders | top-10 share and its source, holder count, and developer holding · nothing fabricated |
| security | mint authority, freeze authority, listing/trust facts, organic score, dev history, and the on-chain mint information |
| news | the key-gated external/web intelligence · without the key, the actual unavailable state; news is never invented |
| x-intel | the source-backed X research layer, keeping its five real lanes · X1 solana/ecosystem news · X2 memecoin / launch / narrative · X3 KOLs / callers / traders · X4 a specific token by ca / ticker / name · X5 community reaction and cross-reference |
| tools | token metrics · quote and underlying · top holders · OHLCV candles · one solana account · how this app launches · a key-gated web search |
the collapsed token card carries a details disclosure with the full read: volume windows, 24h transaction counts, buys and sells, pair age, the top-10 share, the authority flags, the dex and the quote asset
not advice
agents
agents · the ai workforce
/agents and /agents/<id> · the generic agent workspace
- 1agent
- 2task
- 3instructions
- 4memory
- 5tools
- 6real execution
- 7persisted run
the agents surface is a generic workspace for independent AI workers. an agent is not a token and not a due-diligence engine by default: it has its own name, role, description, instructions, tools, memory configuration, run mode, status and run history. the runtime is generic too · it contains no hardcoded token, due-diligence or score workflow. if an agent is set up to perform due diligence, that comes from the agent's own instructions and the task it is handed, never from the runtime
- an agent
- name · role · description · instructions · tools · memory · run mode · status
- the core model
- agent + task + instructions + memory + tools -> real execution -> real result -> persisted run
- the workforce
- /agents · the roster read from GET /api/agents, newest first
- the workspace
- /agents/<id> · overview · instructions · tools · memory · runs · settings
the workforce starts empty because the store ships no seeded agents. every value rendered for an agent is the value the API returned; a field the API did not return keeps its slot and reads "not set" rather than a guess. search filters this response by the agent's own name, description, role and tools text, and the category chips are read from the role text the owner wrote, not a stored category column
opening an agent reads the real entity and its real runs, and every edit PATCHes the entity, so an edit survives a reload. run modes MANUAL and ON_DEMAND run today; SCHEDULED and MONITOR are disabled and say "coming later" because the backend does not run them yet. status is printed verbatim from the entity · READY, RUNNING, PAUSED or ERROR
the run and its real result
a task is run by POST /api/agents/[id]/runs with a flat body · inputType is text, ca or url, inputValue is the task itself and prompt is an optional extra line. the route stores the row and starts execution out of the request path, so the response is the honest queued row while the run advances in-process. a run left queued or running past the hard timeout by a dead process becomes failed with the real reason, so it is never queued forever
| status | what it means | stored |
|---|---|---|
| queued | the row exists, execution has not started · it must not stay queued forever | result null · error null |
| running | the runtime claimed it and is executing; tools may be in flight | result null · error null |
| success | the task finished and the real answer plus the real execution trace are stored | result { answer, steps, sources } |
| failed | it could not finish; the reason is the provider's or the tool's own, never turned into a success | error string · result null |
an agent can only use tools it has access to. the runtime executes real tools through the capability registry · a tool the model was not granted, or one that does not exist, is recorded as a refused step and never executed, and a tool whose source is not configured is reported with its real reason instead of being called. a fake tool trace is not acceptable, and a tool that fails records its real failure
memory is a JSON settings value stored on the entity · the create form writes { enabled, scope, notes }, where scope is shared across this agent or dropped when the run ends. it is stored and edited as JSON on the memory tab, and it is not described as vector, semantic, long-term or permanent memory, because those systems do not exist here
launch
launch
/launch · name the token, read it, sign it with your wallet
- 1Token Details
- 2Market Pair
- 3Agent Setup
- 4Review and launch
launch creates a token through the supported launch venue. the step rail reflects the form state on the fly; it is never a wizard and never gates the submit. you pick a venue, name the token, and sign the create with the wallet · nothing is signed or sent without that approval
| venue | what the path does |
|---|---|
| StonkFun LaunchLab | a quote token chosen from the live launchable pairs, a standard or reward fee model, an optional holder rewards tax, and the venue's own raise and supply read from the venue |
| Pump.fun | a dev buy expressed as a share of the one-billion supply, bought in the same transaction |
market pair
the market pair is the quote/market asset selected for the launch. on the StonkFun / LaunchLab path the selector uses the live launchable pair set the venue returns · the list is read live and never a hardcoded source of truth · and the pricing for the selected quote (its raise, supply and decimals) is read from the venue. a third-party pair whose ticker or name carries a stray character is dropped from the picker rather than rewritten. the panel beside the form shows the live market pairs for reference
token details
the accepted fields are the ones the form reads: token name up to 32, symbol up to 10 (stored uppercased), a description up to 200, a metadata uri, an image of png, jpg or webp up to 2 mb, an optional website and an optional x link (https or empty), and a required check that you know the token can go to zero. the summary describes the transaction actually built and nothing else
agent setup · the launch due diligence engine
agent setup is the launch-specific AI due diligence engine, and it is different from the generic agents workspace. it evaluates the project/token currently being prepared. it runs the real persisted due-diligence run (POST /api/launch/analyze) and follows that run to a terminal status, and it works in one order: COLLECT EVIDENCE · VERIFY · ANALYZE · SCORE · FIND RISKS · VERDICT. every conclusion is tied to actual evidence preserving SOURCE, DATA, TIMESTAMP and STATUS, and a value no source reported stays unknown
the engine's sections are read at render time from its own config, so this list cannot drift: CONTRACT SAFETY, LIQUIDITY & PAIR, HOLDER STRUCTURE, MOMENTUM, SOCIAL and NARRATIVE. its bands map onto four published verdicts
| verdict | tone | what it means |
|---|---|---|
| READY TO LAUNCH | accent | the engine's own cleared band mapped onto the launch verdict · can launch |
| READY WITH WARNINGS | warn | some measured risk · can launch, with the warnings shown |
| NOT READY | negative | material or severe measured risk · hold |
| INSUFFICIENT DATA | neutral | a critical input is absent or coverage is too low · no verdict, no score |
review and launch
the launch summary states only what is true for the transaction it signs: the launchpad, the supply, the program, whether the metadata uri is https or not set, the launch cost · network rent and fees, with no launch fee on the LaunchLab path · and the creator, which is this wallet. the create transaction is built from the venue's own sdk and signed by your wallet, and the metadata is pinned or self-hosted and served back when no pinning pair is set
routes
the routes
the actual server routes · every route runs server-side, and the vendor keys stay on the server
nothing model, key or vendor related is sent to the browser. a missing key is an honest reason, never an empty 200 dressed up as a success. a malformed parameter is a 400 with the exact reason, and an unknown resource returns the actual not-found behaviour. empty successful data stays different from failed data. every read is read-only unless it stores a project, a chat, a run or an observation
market and lists
| method | path | params | what it does |
|---|---|---|---|
| GET | /api/social/trending | ?limit=1..100 | the source's own trending board, as the shared list envelope |
| GET | /api/social/hot | ?window=1h|6h|24h | the GMGN market/trending read, kept strictly separate from the trending board |
| GET | /api/social/callout | ?scan=1..20&threshold=(0,100] | the tracked-holder share scan · numerator and denominator stay explicit, and omitting threshold uses the server default so the UI reads the live number back |
| GET | /api/social/holders | ?token= | top 10 wallets combined share, largest wallet, holder count and the burn / lp / unidentified categories |
| GET | /api/social/kol | ?token= | the recognised buyers who provably bought, never a hold labelled a buy |
| GET | /api/social/xradar | ?token=&ticker= | up to five qualifying high-view posts about the token |
| GET | /api/tape | ?window=1h|6h|24h | the live tape: trending, hot and the bot's callout rows |
| GET | /api/gmgn | ?window=1h|6h|24h | the gated GMGN board read · a blocked gate names the cooldown |
| GET | /api/gmgn/losers | none | real 24h losers, the negative side of the same trending read |
| GET | /api/gmgn/status | none | the request gate state: whether GMGN is cooling down and for how long |
| GET | /api/token-stats | ?mint= | one token's stats block, the same figures the desk prints |
| GET | /api/fomo | ?resource=&window=&limit=&handle=&token=&q=&type=&sort=&chain= | a read-only read of the tracked-trader source · a missing key is an exact reason |
token and decision
| method | path | params | what it does |
|---|---|---|---|
| GET | /api/ai/decision | ?mint= | the one weighted decision model: score, label, band, completeness, per-component maths and the missing list |
| POST | /api/read | body { mint } | one full read over onchain, market and calls · a string that is not a mint is refused with 400 before any outbound call |
| GET | /api/callers | ?mint= | the callers for a mint · no key or no posts returns an empty list, never an invented handle |
| POST | /api/calls | body { mint, symbol? } | resolve the call handles for a mint |
mind
| method | path | params | what it does |
|---|---|---|---|
| GET | /api/mind/projects | none | every project, oldest first, plus the default project id |
| POST | /api/mind/projects | body { name? } | create a project |
| GET | /api/mind/projects/[id] | none | one project with its chats and the distinct mints of those chats |
| PATCH | /api/mind/projects/[id] | body { name } | rename a project |
| DELETE | /api/mind/projects/[id] | none | hard delete the project and its chats · refused with 409 on the last project |
| GET | /api/mind/sessions | ?projectId= | the chats under a project, newest first, without message bodies |
| POST | /api/mind/sessions | body { projectId?, title?, mint? } | create a chat under a project |
| GET | /api/mind/sessions/[id] | none | one chat with its messages in order |
| DELETE | /api/mind/sessions/[id] | none | delete a chat and its messages |
| POST | /api/mind/chat | body { messages, ca?, stream?, sessionId?, temperature?, maxTokens? } | streaming chat with provider fallback and merged grounding |
| GET | /api/mind/context | ?mint=&raw=&fresh= | the ground read for one mint, every field carrying its source and status |
| GET | /api/mind/intel | ?mint=&fresh= | the analyze, holders, security and news intel read |
| GET | /api/mind/x-intel | ?q=&lane=X1..X5&limit= | the X Intelligence lanes over one query |
| POST | /api/mind/x-intel | body { query, lane?, limit? } | the same X Intelligence service |
| GET | /api/mind/help | ?q=&fresh= | plain-english help from a keyword index where every topic names a real file |
| GET | /api/mind/models | none | the model catalog against the current environment · an unconfigured provider carries its exact reason |
agents
| method | path | params | what it does |
|---|---|---|---|
| GET | /api/agents | ?q=&category=&limit=1..200 | the AI workforce collection, newest first · the real search and category filter, capped at 200 rows, and an honest empty list when the store is empty |
| POST | /api/agents | body { name, runMode, description?, role?, icon?, systemInstructions?, tools?, memorySettings? } | create an agent · name and runMode are required, every other field is optional; tools is a string list and memorySettings a plain object |
| GET | /api/agents/[id] | none | one agent · a malformed id is a 400 and an unknown id is a 404, never an empty-but-200 and never another agent's row |
| PATCH | /api/agents/[id] | body { name?, description?, role?, icon?, systemInstructions?, status?, tools?, runMode?, memorySettings? } | update any subset · an empty patch returns the agent untouched rather than writing a no-op; lastRunAt is server-owned and not patchable |
| DELETE | /api/agents/[id] | none | hard delete the agent and its run history · an unknown id is a 404 |
| GET | /api/agents/[id]/runs | none | the run history, newest first · an unknown agent is a 404, not an empty list |
| POST | /api/agents/[id]/runs | body { inputType: text|ca|url, inputValue, prompt? } | enqueue a run and start execution out of the request path · the response is the honest queued row while the run advances in-process; prompt is persisted for execution but never returned |
desk, feed and store
| method | path | params | what it does |
|---|---|---|---|
| GET | /api/callout | none | the bot's own persisted calls and skips |
| POST | /api/callout | body { rows } | run one callout pass over the tape the desk already loaded; the bot never signs or trades |
| GET | /api/callouts | none | the global callout feed |
| GET | /api/callout-log | none | the append-only log: calls, skips with the failing rule, and the rule config |
| POST | /api/chat | body { messages, ca } | the plain desk chat; it has no live data access and explains only |
| POST | /api/ipfs | body image + metadata | pin launch metadata, or self-host the bytes when no pinning pair is set |
| GET | /api/ipfs | ?asset= | read a self-hosted launch asset back |
| GET | /api/img | ?u= | the image proxy the token rows read through |
| GET | /api/agent | ?token=&observe=&selftest=1 | the per-token agent store: store health, one token's memory counts, the read-only brain read, and the token-isolation proof |
| POST | /api/agent | body { token, kind, value?, status?, source?, reason?, observedAt? } | write one observation to that token's memory; an absent value stays absent |
| GET | /api/x-intel | ?q=&lane=X1..X5&limit= | the X Intelligence service, source-agnostic |
| POST | /api/x-intel | body { query, lane?, limit? } | the same X Intelligence service |
states
the honest states
what every state means, and why a metric can be absent
the source answered successfully and returned the value
nothing is absent · the number is the source's own
a real previously observed value older than the normal freshness window, still served and marked
the source is cooling down or slow, so the desk serves the last good read and labels it stale
the source answered successfully but did not report this particular field
for example a token and its market cap arrive with no KOL field, so KOL reads missing, never 0
the source could not be read
no key is configured, the provider returned an error, the read timed out, the source is rate limited, the response is invalid, or configuration failed · the real reason is preserved
the application intentionally refused to send the request, so nothing left the desk
a request gate, throttle or upstream restriction stopped it · different from a provider failure
the AI decision state when a critical model input is absent or the available evidence cannot support a valid score
the model withholds the score · not the same as unavailable and not the same as missing
why a metric can be absent
- no credential is configured for that source · the block is omitted with its reason and no request is sent
- the source is rate limited · the reason is the rate limit, and a cooldown may be named
- the source does not expose that field · a dash with the neutral reason
- there is no pair on the dex yet · depth and flow cannot be read
- the read failed outright · the surface prints its own honest line, never a number
- the data is stale or temporarily unavailable · the last real value is served and labelled stale
- the AI model lacks a critical input · the state is INSUFFICIENT DATA with the score withheld
a fresh read can also be warming on the first pass, before any scan has landed, and a list can be legitimately empty. both are stated as themselves and are never automatically interpreted as an error or confused with a failed read
REAL DATA IN.
GROUNDED ANALYSIS OUT.
UNKNOWN STAYS UNKNOWN.
not a buy call. memecoins go to zero.