Developers

Public source, OpenAPI spec, TypeScript SDK, embeddable widgets, a CLI, and a standalone calculator. Everything you need to build on top of Latrine Bot lives in one repo and three npm packages.

Repo: github.com/dfnwtf/latrinebot (MIT for code, CC BY 4.0 for docs). The engine, runner, and operator panel are private by design - the public repo is enough to integrate, audit the API, and understand the eligibility math.

What is published

Package Use it for Install
@latrinebot/sdk Typed HTTP client + OpenAPI 3.1 spec npm i @latrinebot/sdk
@latrinebot/widgets Browser-embed library for live stats / holder list / status badge npm i @latrinebot/widgets
@latrinebot/cli Thin CLI over the public API. Great for cron jobs, CI, alerts npm i -g @latrinebot/cli

Source on GitHub: sdk/ · widgets/ · cli/ · calculator/ · docs/

Getting credentials

Three things to know about, in increasing privilege.

Project ID (UUID)

Created when you make a project in the dashboard. The dashboard URL while a project is open contains it: https://latrinebot.com/app/?project=<id>. That UUID is the value to pass into the SDK, the CLI, and the widget embeds.

Metrics key (lb_live_...) - read only

Per-project, read-only token for client.metrics.* (status, stats, events, stream). Generated in the dashboard under API -> Generate metrics key. Shown once; rotate or revoke at any time.

Safe to ship in server-side scripts, dashboards, and CI. Do not put it in a public browser bundle - use the public widget endpoints instead.

Bearer JWT - full session

Required for client.projects.*, client.lifecycle.* (start, stop, run-once, preflight), client.keys.*, and any other write route. Two ways to get one:

JWTs roll on a 7-day window. Long-running scripts should re-run SiwS when they get LatrineError with code === "auth_invalid".

Fully public (no auth)

Eligibility lookups, share-card bundles, widget config / live endpoints. Useful for build steps and embeddable third-party pages. See API reference for the full list.

SDK quick start

Read a project snapshot with a metrics key:

import { LatrineClient } from "@latrinebot/sdk";

const client = new LatrineClient({
  metricsKey: process.env.LATRINE_METRICS_KEY,
});

const snap = await client.metrics.get(process.env.PROJECT_ID);
console.log(snap.stats.totalClaimedSol, "SOL claimed");
console.log(snap.stats.cycles, "cycles ran");

Read the token page LIVE payload (stats, fee split, hold fund, policy history - no metrics key, no login):

const client = new LatrineClient();
const live = await client.public.realm.live(projectId);
console.log(live.poolSplit.distributePct, "% to holders");
console.log(live.holdFund?.purposeLine, "hold purpose");
console.log(live.holdFund?.heldSol, "SOL in hold reserve (dev wallet or vault)");
console.log(live.holdFund?.guaranteed?.executionStatusLabel, "Dex Vault status");
console.log(live.policyHistory[0]?.body, "latest policy change");
console.log(live.stats.totalDistributedSol, "SOL to holders (est.)");

Sign in, then start a project:

const client = new LatrineClient();
const { message } = await client.auth.challenge(walletPubkey);
const signature = await wallet.signMessage(new TextEncoder().encode(message));
const { token } = await client.auth.verify({ wallet: walletPubkey, signature });
client.useBearer(token);

await client.lifecycle.preflight(projectId);
await client.lifecycle.start(projectId);

Anonymous eligibility check (no credentials):

const res = await client.public.checkEligibility(projectId, {
  wallet: "SomeWalletPubkey",
});
if (!res.eligible) console.log("rejected:", res.reason);

Full method list and error codes: SDK README.

CLI quick start

For cron jobs, CI, and quick checks.

npm install -g @latrinebot/cli

# Save credentials once
mkdir -p ~/.latrine
cat > ~/.latrine/config.json <<EOF
{
  "metricsKey": "lb_live_xxxxxxxx",
  "token":      "eyJhbGciOi..."
}
EOF

# Read-only
latrine status     <project-id>
latrine events     <project-id> --limit 20
latrine watch      <project-id>
latrine eligibility <project-id> <wallet>

# Authenticated
latrine preflight  <project-id>
latrine projects list
latrine start      <project-id>
latrine stop       <project-id>
latrine run-once   <project-id>

PowerShell equivalent of the config file write:

New-Item -ItemType Directory -Force "$env:USERPROFILE\.latrine" | Out-Null
'{"metricsKey":"lb_live_xxxxxxxx","token":"eyJhbGciOi..."}' |
  Set-Content "$env:USERPROFILE\.latrine\config.json"

Full command list: CLI README.

Embeddable widgets

Two ways to embed live data on a third-party page.

Iframe (zero JavaScript)

<iframe
  src="https://latrinebot.com/embed/?id=YOUR_WIDGET_UUID"
  width="480" height="560"
  style="border:0;background:transparent"
  loading="lazy"></iframe>

npm library (custom mount, theming, programmatic refresh)

import { mountWidget } from "@latrinebot/widgets";

mountWidget("#stats", {
  widgetId: "YOUR_WIDGET_UUID",
  theme: "stream-dark",
});

Both modes call the public widget endpoints - no credentials needed. Build your widget in the Widget builder docs, then copy either the iframe URL or the widget UUID.

OpenAPI 3.1 spec

The full API surface ships as a machine-readable spec. Useful for generating clients in other languages, driving Postman / Insomnia collections, and validating requests in a gateway. Public holder perks routes (reward-options, reward-preference, social-claim) are included under the public tag. Narrative examples: Holder perks API.

# From the npm package
npm pack @latrinebot/sdk
tar -xOzf latrinebot-sdk-*.tgz package/openapi.yaml > openapi.yaml

# Or from GitHub directly
curl -O https://raw.githubusercontent.com/dfnwtf/latrinebot/main/sdk/openapi.yaml

Eligibility calculator

Live at latrinebot.com/calculator/. Pure HTML + JS, no build step. Source in the calculator folder. Same math as the engine; useful for showing potential users where a wallet would land.

Versioning and support

< Back to docs