Configuration
The public dashboard runs mainnet LIVE only. Every field below maps to project data stored in the cloud. Click Apply settings after bot changes, Save tiers when you only edit the tier table, and Save credentials for the dev wallet and RPC.
Identity
| Dashboard label | Field | Notes |
|---|---|---|
| Project name (create bar) | name | Any label you choose. Optional on create; default My Project. |
| Mint (CA) required (create bar) | mint | Required when you click Create. Valid Solana base58 (32-byte pubkey). Stored on the project and in settings.mint. |
| Name (Bot settings) | name | Project display name. Saved with Apply settings. |
| Mint (CA) (Bot settings) | mint | Editable. Must stay a valid CA; cannot be cleared. Changing it and saving refreshes the ticker from DexScreener. |
| Token symbol | tokenSymbol | Read-only in the dashboard. Set by the server from DexScreener (max 16 chars). Updates when the mint changes. Used in logs (e.g. buyback lines). |
On POST /api/projects, the API rejects a missing mint. Client PATCH cannot set tokenSymbol; only a mint change triggers a new lookup.
Cycle timing
| Dashboard label | Field | Default | Range |
|---|---|---|---|
| Cycle interval (s) | cycleIntervalSec | 60 | 5 - 3600 |
After you press Start, the first on-chain cycle runs in about 3 seconds, then repeats every interval.
Claim, reserve, buyback
| Dashboard label | Field | Default | What it does |
|---|---|---|---|
| Min claim (SOL) | minClaimSol | 0.005 | Skip claim until Pump.fun creator vault balance is at least this amount. |
| Min reserve (SOL) | minReserveSol | 0.05 | SOL the bot tries to leave on the dev wallet for future fees. Also used by launch readiness balance checks. |
| Rewards to distribute % | buybackPercent | 100 | Share of pooled creator fees (after tx fees) for holder rewards this cycle. Independent of burn %. Sum with burn % must be ≤ 100; remainder stays in the pool. |
| Burn % | burnPercent | 0 | Extra share of the same pool for project-token buyback and SPL burn (not airdropped). Works with any reward asset. Sum with distribute % must be ≤ 100. |
| Slippage (bps) | slippageBps | 500 | Basis points for buyback and Jupiter swaps. 500 = 5%. Max 5000. |
For the project token (the default reward), the buyback route is chosen automatically: bonding curve before graduation, PumpSwap AMM after the curve completes. For SOL / USDC / custom-token rewards, see Airdrop reward below.
Airdrop reward (what holders receive)
Eligibility and holder snapshots are always computed against the project mint - holders must hold your token to qualify.
Only the distributed asset is configurable. Choose it under Bot settings - Airdrop reward; it is stored as settings.rewardAsset.
| Dashboard option | rewardAsset.kind | How it is acquired each cycle |
|---|---|---|
| Project token (buyback) | SAME (default) | Buy your token from claimed SOL (Pump.fun bonding curve or PumpSwap) and airdrop it. Legacy behaviour. |
| SOL | SOL | No buyback. Claimed SOL is split directly to holders (native transfers, no ATA rent). |
| USDC | USDC | Swap claimed SOL to USDC via Jupiter, then airdrop USDC. |
| Custom token (mint) | CUSTOM | Swap claimed SOL to any SPL mint via Jupiter, then airdrop it. Requires a tradable Jupiter route. |
Reward mint (CA) (rewardAsset.mint) is required only for CUSTOM and must be a valid base58 SPL mint with a Jupiter swap route.
It is independent from the project mint: holders still qualify on your token, but receive the reward mint.
An invalid or routeless mint is rejected; an unrecognized rewardAsset safely degrades to SAME so existing projects keep working with no migration.
All reward types stay self-financing: gas, ATA rent, and (for USDC / custom) the Jupiter swap are paid from claimed creator fees before sizing the spend. Logs and overlay show the reward token's own ticker (e.g. $USDC or your custom symbol), not the project ticker.
Launch readiness adds a reward_asset check that probes a read-only Jupiter route for USDC / custom rewards (required to pass) and is informational for SOL / project-token rewards.
Public distribution transparency
When you change holder-facing policy in the dashboard and press Apply settings, Latrine Bot writes a permanent public audit line automatically. There is no toggle to disable this, no setting to change severity rules, and no way to hide a logged change from the token page or your dashboard Output after it is saved.
What triggers a public log line
Each successful PATCH /api/projects/:id that changes any of the following appends one or more POLICY events to project_events:
| Change | Example log |
|---|---|
Fee split (buybackPercent, burnPercent, hold %) | Distribution changed. Holders 80% -> 100%. Burn 10% -> 0%. Hold 10% -> 0%. |
| Major holder cut (server rule) | Major distribution cut. Holders 100% -> 61%. … with severity alert |
Payout currency (rewardAsset) | Payout currency changed: project token ($TICKER) -> SOL. |
| Holder reward choice on/off | Holder reward choice enabled. / … disabled. |
| X post boost on/off | X post boost enabled. / … disabled. |
Hold fund transparency (holdFund) | Creator hold fund: simple reserve (no public goal)., Creator hold fund: Dex Vault selected. …, Dex Vault active. Hold N% locked - …, or goal-mode Creator hold fund: Dex Paid. Target: 4 SOL. (only when hold % > 0) |
Major distribution cut is decided by the platform, not by you: holder airdrop % drops by 20 points or more, or goes to 0%.
You cannot change that threshold or suppress the alert styling.
Where holders and you see it
- Token page banner - current fee split bar, default payout, and perk toggles (
poolSplit,publicFeaturesonGET .../realm/:id/live). - Hold fund goal - when you use Hold with a goal and hold % is above zero, the token page shows a Hold fund transparency card with purpose, SOL saved, and an optional progress bar (
holdFundon the same live endpoint). Dex Vault mode shows a separate public card with vault balance and progress toward ~$299. - Distribution history - sidebar list of past
POLICYlines (same endpoint,policyHistory). - Activity log - highlighted
POLICYrows in the public feed; sticky Creator policy change banner for the latest change (policyAlert, permanent until the next change). - Dashboard Output - the same
POLICYlines viaGET .../eventsor the live SSE stream (usually within ~1-2 s while the bot is running).
The token page refreshes about every 20 seconds. After Save, expect a short delay before the public page shows the new banner or history entry. The dashboard asks for confirmation before saving when a public policy change is detected.
API shapes: Distribution transparency API.
Hold fund transparency
When your Creator fee split keeps a non-zero hold slice, part of each cycle's pooled creator fees is reserved for your hold fund.
In simple and goal modes the reserve stays on the dev wallet (stats.totalHeldSol).
In Dex Vault mode hold fees transfer to a locked vault wallet each cycle - segregated from the dev wallet - until DexScreener is paid.
The dashboard block Hold fund transparency lets you explain why you keep that slice and,
optionally, show progress toward a SOL goal or Dex payment on your Stream Studio overlay and token page.
This is not holder hold cycles in the tier table. Hold cycles gate eligibility streaks; hold fund is the creator's fee-split reserve counter.
When the block appears
The hold fund panel is visible in Bot settings only when hold % (the remainder after distribute + burn) is greater than zero. If you set distribute + burn to 100%, hold fund settings reset to simple mode internally.
Modes
| Dashboard mode | settings.holdFund | Token page | Stream Studio (hold-vault layer) |
|---|---|---|---|
| Simple hold (amount only, no goal) | { mode: "simple", template: null, customLabel: "", goalSol: 0 } |
No extra hold card (only the hold % in the fee split bar). | Compact read-only card: Creator hold reserve, held SOL, hold %. |
| Hold with a goal | { mode: "goal", template, customLabel?, goalSol } |
Hold fund transparency card: purpose line, held SOL, progress bar when goalSol > 0. |
Same data; card grows for long purpose text and goal row. |
| Dex Vault ($299 Dex Paid) | { mode: "guaranteed", guaranteed: { … } } |
Dex Vault card: full purpose line, vault balance, progress toward ~$299. | Same data; short purpose Reserved for Auto Dex Payment + status Accumulating. |
Dex Vault (guaranteed mode)
Dex Vault is a locked vault for hands-free DexScreener Enhanced Token Info (~$299).
After you complete Dex Prefill and click Activate Dex Vault, hold fees from your creator hold % slice
transfer to a dedicated vault wallet each cycle - segregated from the dev wallet. The dev wallet cannot spend those funds.
When the vault reaches the quoted target, Latrine pays Dex Paid automatically.
Activation is irreversible. API field remains mode: "guaranteed".
- Before activation - hold fees stay on the dev wallet; a
POLICYline records that Dex Vault is selected. - Hold % lock - you cannot decrease hold % below the locked value while Dex Vault is active; you may increase it.
- Public data - vault public key, balance, execution status (e.g.
Accumulating,Ready to pay Dex), and progress appear on the token page and inholdFundon the live API. - Dashboard API - session routes under
/api/projects/:id/hold-fund/guaranteed/*(upload, preflight, draft, enable). See API reference.
Purpose templates (goal mode)
Pick one purpose under Purpose. Amounts are always in SOL, not USD.
| Dashboard option | template | Public label |
|---|---|---|
| Dex Paid | dex | Holding for Dex Paid |
| Dex Boost Paid | boost | Holding for Dex Boost Paid |
| Dex Ad Paid | ad | Holding for Dex Ad Paid |
| Custom label | custom | Holding for <your text> (max 80 chars) |
Goal (SOL) is optional but required for a progress bar. Without a positive goal, viewers still see the purpose and held SOL on the token page and stream.
Changing hold fund settings triggers a POLICY audit line (see above) when hold % > 0.
Only you edit hold fund in the dashboard. Stream Studio and the token page are read-only.
In Dex Vault mode public holdFund.heldSol is the vault balance, not stats.totalHeldSol.
Enable the hold-vault layer in Studio to show the card on stream.
Holder reward choice (Holder perk)
Optional perk for eligible holders on your public token page (/realm/:projectId) and the homepage showcase when enabled.
Holders pick what they receive each cycle: project token (buyback), SOL, or USDC.
Eligibility and snapshot weight stay on your project mint - only the payout asset changes per wallet.
| Dashboard label | Field | Default | What it does |
|---|---|---|---|
| Holder reward choice | holderRewardChoiceEnabled | false | When on, the token page shows the Holder perk panel. Wallets connect Phantom once and sign a SIWS message to save a preference in D1 (holder_reward_prefs). |
The dev default from rewardAsset is shown as DEV DEFAULT in the UI.
Wallets without a saved preference receive the dev default cohort.
Saved prefs are read each cycle and split the airdrop budget across SAME / SOL / USDC cohorts.
API: Holder perks API.
X post boost (Boost)
Optional social perk: anyone can post on X about your token and paste the link + wallet on the token page. The boost is active for a fixed time window (default 1 hour) and affects every airdrop cycle that runs while the window is open. Payouts come from the same creator-fee pool as regular holder drops.
Core rules
| Rule | Detail |
|---|---|
| Active window | 1 hour by default (socialBoostDurationMin: 60). Dev can set 5-1440 minutes via PATCH. Timer starts when the claim is accepted. |
| One post URL per token, forever | Each X link (tweet_id) can be claimed once per project, globally. Any wallet - first valid claim wins. Re-submitting the same link returns 409, even after expiry or payout. |
| One active claim per wallet per token | While boost_until is in the future, that wallet cannot claim a second post on this token. After the hour ends, claim again with a new post URL. Response 409 includes boostUntil. |
| Different wallets, same token | Each wallet can claim its own post URL on the same token (each URL still once). Many wallets can have parallel active boosts on one token. |
| Same wallet, other tokens | Limits are per token, not global. A wallet with an active boost on token A can still claim on token B. |
| Boost again later | After the window expires (or after a successful payout), publish a new X post and submit its new URL. Old URLs stay burned. |
| No wallet sign-in | Unlike holder reward choice, boost claim does not require Phantom or SIWS - only a valid tweet URL and wallet address. |
| LIVE only | Feature works only when the project is LIVE with a mint set and socialClaimEnabled: true. |
Step-by-step (holder or non-holder)
- Dev enables Enable X post boost in Bot settings.
- On the token page, open the Boost panel.
GET .../social-claimreturns a suggestedtemplate(ticker, CA,Running on @Latrine_bot). - User publishes a post on X that passes validation (see below).
- User pastes the full post link (
x.com/...ortwitter.com/...) and their wallet, then clicks Claim boost. - Server fetches the tweet via X API, validates text, and stores a row with
status: pendingandboost_until(now + window). - On each airdrop cycle while
now < boost_untiland status is stillpending, the engine applies the boost (see outcomes below). - After a cycle that actually pays that wallet, the claim moves to
status: paid. The tweet URL remains unusable even if the hour has not ended. - If the hour ends before any paying cycle, the boost simply stops applying. The tweet URL is still burned.
What happens in each cycle
Social boost runs after the normal hold-cycle gate. It does not change eligibility history or bypass hold streaks for full tier status.
| Wallet situation | Active boost effect | Reward cohort |
|---|---|---|
| Eligible holder (balance + hold streak passed) | Proportional weight multiplied by socialHolderBoostMultiplier (default 1.15x) |
Same as normal (holder reward choice if enabled) |
| Not in the drop yet (no balance, below tier floor, anti-whale, or hold streak not met) | Added as an intro recipient with virtual weight = socialNonHolderWeightRatio x active tier minTokens (default 8% of tier floor) |
Dev default reward only (socialOnly - not holder reward choice) |
Intro share is a small discovery airdrop, not full holder eligibility. Holders who are still gated by hold cycles are treated like non-holders for boost purposes until they pass the streak.
Dashboard settings
| Dashboard label | Field | Default | What it does |
|---|---|---|---|
| Enable X post boost | socialClaimEnabled | false | Shows the Boost panel on the token page. Wallets submit a tweet URL + address; the server fetches the post and validates it. |
| Boost window (minutes) | socialBoostDurationMin | 60 | How long a claimed post stays active (5-1440). Not exposed in the dashboard UI yet; adjustable via PATCH. |
| Holder boost multiplier | socialHolderBoostMultiplier | 1.15 | Multiplier on an eligible holder's proportional weight when they have an active boost (1-3). |
| Non-holder intro weight | socialNonHolderWeightRatio | 0.08 | Virtual weight for non-holders as a fraction of the active tier minTokens (0-0.5). They join the default reward cohort only (socialOnly). |
Post requirements
Validated server-side when the claim is submitted (case-insensitive):
$TICKER- your project symbol with the dollar sign- Full project mint (CA) anywhere in the text - the word
CA:is optional (the template includes it for clarity) @Latrine_bot- platform handle fromX_TWITTER_HANDLE- At most one distinct Solana-length address in the tweet (blocks multi-CA abuse)
- Tweet must still exist (not deleted); server returns
404if gone
Common errors
| HTTP | When |
|---|---|
400 | Missing or invalid tweet URL, wallet, or post text (no $TICKER, CA, or @handle) |
403 | socialClaimEnabled is off |
404 | Project not LIVE, or tweet deleted |
409 | Tweet URL already used for this token, or this wallet already has an active boost on this token (see boostUntil) |
429 | Rate limit (10 claim POSTs per IP per minute) |
502 | X API could not return tweet text - retry shortly |
API reference: Holder perks API. Eligibility interaction: X post boost.
Holder filters (fallback)
| Dashboard label | Field | Default | What it does |
|---|---|---|---|
| Min holder (fallback) | minHolderBalance | 500,000 | Fallback minimum balance (whole tokens) if no tier matches the current market cap or MC is unknown. |
| Max holder (anti-whale) | maxHolderBalance | 20,000,000 | Anti-whale cap in whole tokens. Wallets holding more than this are excluded. Set 0 to disable. |
All token amounts in the dashboard are whole tokens (the number you would see on Pump.fun or in a wallet). The bot converts to base units internally when comparing on-chain balances.
Once DexScreener reports market cap, the active tier floor from eligibilityTiers replaces the fallback min holder for that cycle.
Eligibility tiers
Stored as eligibilityTiers: an array of { mcUsd, minTokens, holdCycles } rows. Use Save tiers to PATCH only tiers, or Apply settings to save tiers together with other bot fields. See Eligibility.
| MC ≥ (USD) | Min tokens | Hold cycles |
|---|---|---|
| 0 | 500,000 | 5 |
| 50,000 | 450,000 | 6 |
| 100,000 | 400,000 | 8 |
| 250,000 | 300,000 | 10 |
| 500,000 | 220,000 | 12 |
| 1,000,000 | 150,000 | 15 |
| 2,500,000 | 110,000 | 22 |
| 5,000,000 | 75,000 | 30 |
| 10,000,000 | 55,000 | 40 |
| 15,000,000 | 40,000 | 50 |
| 25,000,000 | 30,000 | 60 |
| 50,000,000 | 22,000 | 80 |
| 100,000,000 | 15,000 | 100 |
Reset to default calls POST /api/projects/:id/tiers/reset-default and restores the platform default table above.
Launch readiness (preflight)
Before Start or Run now, open Launch readiness in the Controls panel (or call
GET /api/projects/:id/preflight from your integration).
The dashboard blocks Start and Run now until every required check passes.
- Select your project. Preflight runs automatically after load and after you save credentials or bot settings (debounced).
- Click Check again to refresh without starting a cycle.
- When the summary says Ready to launch, you may Start or Run now. If it says Not ready yet, fix failed required lines first.
What preflight checks
| Dashboard label | Check id | Required | What it verifies |
|---|---|---|---|
| Mint (CA) | mint_set | yes | Project has a mint address |
| Mint format | mint_format | yes | Valid base58 Solana pubkey |
| Credentials | credentials | yes | Dev wallet secret and Helius RPC URL saved |
| RPC connection | rpc | yes | getHealth / slot ping against your RPC URL |
| Mint on mainnet | mint_onchain | yes | Mint account exists (SPL Token or Token-2022) |
| Market data | mint_market | no | DexScreener ticker found (informational; bot can still run) |
| Airdrop reward | reward_asset | conditional | For USDC / custom rewards: required Jupiter swap-route probe. For SOL / project-token rewards: informational. |
| Dev wallet is creator | dev_is_creator | yes | Saved dev pubkey can claim Pump.fun creator fees for this mint |
| Dev wallet balance | balance_reserve | yes | On-chain SOL ≥ minReserveSol |
| Recommended balance | balance_recommended | no | Covers minReserveSol + minClaimSol (launch buffer) |
| On-chain engine | runner | yes | Cloud engine available (may show warming up on cold start) |
Response field ready is true only when every check with required: true has ok: true.
Optional checks (market data, recommended balance) help you tune settings but do not block launch.
Full API shape and curl examples: API reference - Launch readiness.
PATCH /api/projects/:id (client fields)
Client PATCH accepts name, mint, and settings. It cannot change mode (always mainnet LIVE for end users).
Inside settings, the API merges and clamps these keys (via server sanitizeSettings):
mint- kept in sync with top-levelmintwhen you PATCH mintcycleIntervalSec,minHolderBalance,maxHolderBalanceminClaimSol,minReserveSol,slippageBps,buybackPercent,burnPercentrewardAsset- normalized to{ kind, mint };kindinSAME | SOL | USDC | CUSTOM,mintrequired (valid base58) only forCUSTOM, otherwisenull. Invalid input degrades toSAME.holderRewardChoiceEnabled- boolean, holder perk on the token pagesocialClaimEnabled- boolean, X post boost on the token pagesocialBoostDurationMin- integer minutes (5-1440)socialHolderBoostMultiplier- number (1-3)socialNonHolderWeightRatio- number (0-0.5)holdFund-{ mode, template, customLabel, goalSol }- see Hold fund transparency. Normalized server-side; simple mode clears template fields.eligibilityTiers- normalized (sorted, deduped); legacyholdCyclesRequiredis stripped
tokenSymbol is server-owned: any tokenSymbol in the PATCH body is ignored.
Operator-only keys (legacy simulation tuning and other internal knobs) are not documented here and are omitted from public API responses.
PATCH /api/projects/:id
{
"name": "My token",
"mint": "YOUR_CA_BASE58",
"settings": {
"cycleIntervalSec": 90,
"minClaimSol": 0.01,
"buybackPercent": 80,
"rewardAsset": { "kind": "CUSTOM", "mint": "REWARD_MINT_BASE58" },
"holderRewardChoiceEnabled": true,
"socialClaimEnabled": true,
"holdFund": {
"mode": "goal",
"template": "dex",
"customLabel": "",
"goalSol": 4
},
"eligibilityTiers": [
{ "mcUsd": 0, "minTokens": 500000, "holdCycles": 5 }
]
}
}
Use { "kind": "SOL" }, { "kind": "USDC" }, or { "kind": "SAME" } for the other reward types (mint is ignored unless kind is CUSTOM).
Credentials (separate save)
Not part of Apply settings - use Save credentials (POST /api/projects/:id/secrets):
devWalletSecret- Base58 private key of the Pump.fun launch / claim wallet (64 bytes when decoded).rpcUrl- Full Helius (or compatible) mainnet URL with API key in the query string.
Click Test RPC after save to verify connectivity. Credentials metadata (pubkey, masked RPC host) is available at GET /api/projects/:id/credentials.