The seven Kits
What each Kit is, what it does today, and where its HTTP surface and its tests live.
Putty is an engine, and the Kits are the product. A Kit is a finished
outcome rather than a tool: the build plan
(context/build-plan-artsy.md §3.1) describes one as a guided flow, a
finished-outcome definition the engine verifies rather than claims, and a public
artifact.
This page says what each one does today, and where you can check it. Where a Kit falls short of its plan, that is written here too.
The seven, in build order
The plan ships one Kit per day (§7.1). The order is tools first; the Launch Kit comes last because it assembles what every other Kit builds.
1. Business Kit
A shop, its orders, and a checkout that takes a card or a stablecoin.
Ten operations under /business/*: register a shop, read and patch it, delete it,
read its order list and takings summary, open a checkout, read one, and list the
payment methods the deployment can actually take.
Two things make it worth reading the source for. First, the response shape is
identical for both payment methods — checkoutUrl is the link the Pay button
points at whether the shopper pays by card or in USDC or USDG, and the customer
completes the payment on the provider's own page. Putty never takes custody: it
never sees a card number, never holds a stablecoin, and never asks for a seed
phrase. Second, GET /business/methods reports what the deployment has a
provider wired for, so a card-only deployment renders one button rather than a
dead second one.
There is no dashboard page for this Kit. The API is the whole surface.
Where to look
apps/api/src/modules/business/ ·
POST /business/checkout ·
GET /business/methods ·
apps/api/test/modules/business-kit.test.ts
2. Community Kit
Holder-gated bots, launch countdowns, a holder census, and a one-ballot-per-owner
spotlight vote. Eleven operations under /community/*.
POST /community/verify is the centre of it, and it has three deliberate
outcomes. A balance above zero passes with a 200. A balance of zero is 403
not_a_holder, not a 200 with verified: false — the refusal is the
product, so it has to be a status a gateway can act on without parsing the body.
A chain read that fails is 502 chain_unavailable, a third outcome on purpose:
a caller must be able to tell "you are not a holder" from "we could not check",
and collapsing the two would show a member an error instead of a refusal.
There is no /app/community page. The Trade page's balance check is the one
dashboard code path that calls it.
Where to look
apps/api/src/modules/community/ ·
POST /community/verify ·
apps/api/test/modules/community-kit.test.ts
3. Contract & Hook Studio
A contract shape, written from a prompt, with a verdict attached before anyone signs.
The Verify surface is live and split in two on purpose. POST /verify and
GET /verify/methods are public, free, and take no bearer at all — paste a
contract and get the permission diff and the plain-English readback.
POST /verify/ai-review is the opposite: it is the only authenticated route in
the module, because it spends the caller's credits. It runs several independent
model passes and returns findings ranked by severity and by how many passes
agreed, beside the deterministic report rather than instead of it. The free
checks stay open — safety is not an upsell.
A note on naming: the AI review is labelled AI review, never "audit". A model reading code is not an audit, and Putty does not blur it.
The Studio's own two routes are live and mounted. GET /contracts/presets
returns the full catalogue — 11 presets, each with its title, description,
source (wizard or ours), the OpenZeppelin files an ours preset is built
on, its audit label, its licence, and every constructor parameter with its
Solidity type, required flag, default, and placeholder. It takes no bearer.
POST /contracts/generate requires a bearer and is metered, not free. It
returns the generated Solidity, the rendered constructor arguments, a Foundry
test skeleton, the audit label, a SHA-256 fingerprint, the OpenZeppelin version
the Wizard pins, its Foundry remappings, and what it was charged. A missing
required parameter is a 400 naming each offending one; an unaffordable one is a
402 in billing's own shape. Generation is deterministic: the same preset and the
same arguments produce the same source, and the route says so.
On the audit label. There are three, and they mean different things. audited
is an OpenZeppelin library contract used as-is, and is reserved for a case where a
real dependency is audited. reviewed is unmodified Wizard output — the parts
are audited, the composition is not. experimental is Putty's own code, or
generated code that has been hand-edited. The label is a ceiling: a preset can
only be as trusted as its cleanest form, and a hand edit to a generated file
drops it to experimental, which a mainnet deploy then refuses.
Where to look
apps/api/src/modules/verify/ ·
POST /verify ·
POST /verify/ai-review ·
GET /contracts/presets ·
POST /contracts/generate ·
apps/api/test/modules/verify-kit.test.ts ·
apps/api/test/modules/contract-studio.test.ts
4. Trust Kit
Onchain money logic — splits, buybacks, vesting, treasury rules — with a public Receipts page that says what can change, who can change it, and how fast.
The contracts are Foundry, in packages/contracts/src/: ten contracts —
PuttyToken, PuttyTokenFactory, PuttyTreasuryRouter, PuttyFeeBase,
PuttyFeeSplitter, PuttyVesting, PuttyBuybackVault, PuttyBurnVault,
PuttyHolderRewards and PuttyTrustModule — green on a local EVM with 264
Foundry tests. The readable surface is GET /trust/receipts/{chain}/{token},
which reads a token's setup straight off the chain and returns it as a document —
every module found, every wallet it references with its live balance.
There is also an HTML rendering of the same document at
GET /trust/receipts/{chain}/{token}/page, served with script-src 'none' and
every onchain string escaped, so a token's setup can be linked to anyone without
the reader running any of it.
Three properties make this route worth reading:
- It works for any token on any supported chain. There is no allowlist and no putty-only path. A module is recognised by the getters its contract answers, so a token launched on Pons or Hookr reads the same as one launched here, and a token Putty never heard of reads too.
- A token with no modules returns an empty
modulesarray, not a 404. The token is real; its setup is simply empty. A 404 means no contract at that address at all. - Every amount is a decimal string in the token's base units, never a float.
Modules are ranked by how many of their discriminating getters answered, so the identification is not order-dependent, and a tie is treated as no match at all.
Do not confuse it with GET /receipts, which is unrelated — that one lists
payment receipts.
Where to look
packages/contracts/src/ ·
GET /trust/receipts/{chain}/{token} ·
apps/api/test/modules/trust-receipts.test.ts
5. Creator Kit
An NFT drop with a branded mint page. Nine operations under /creator/*: create
a drop, list, read, patch, delete, record a mint, list mints, and the public page.
A drop references a brand kit rather than restating it, so one brand kit dresses
many drops. The mint page is public, static, and serves the name, image, supply,
price, and mint state — GET /creator/mint/{dropId} is the route that answers
with no bearer, so a mint page can be linked anywhere.
The Brand studio itself is its own surface, not part of this Kit:
GET/POST /brand/kits, POST /brand/kits/draft, and the per-kit routes.
Where to look
apps/api/src/modules/creator/ ·
GET /creator/mint/{dropId} ·
apps/api/test/modules/creator-kit.test.ts
6. Agent Kit
Register an agent, meter its calls, and read what it spent. Eight operations
under /agents/*.
Registering an agent grants nothing. It is naming a workload so its spend is
separately reportable. An agent can only make metered calls with a bearer the
caller already holds — an sk- key from POST /account/api-keys, or the
caller's own Privy session — and it draws on the caller's credit balance.
POST /agents/{id}/calls is the route an agent harness on a user's own host
calls, and it has one property that matters more than the rest: the charge comes
first. Credits are taken before the tool runs, so a caller whose balance is
short gets a 402 with nothing metered and no tool run. There is no state in which
the work happened and the money did not.
requestId is the idempotency key and it is the caller's job to make it stable.
A retry of the same work carries the same id; the replay returns the same charge,
takes no new credits, and says replayed: true.
Where to look
apps/api/src/modules/agent/ ·
POST /agents/{id}/calls ·
apps/api/test/modules/agent-kit.test.ts
7. Launch Kit
Build the unsigned transaction that creates a token, and quote a swap through a Putty pool.
POST /launches validates the launch parameters against the provider's limits
and returns the unsigned transaction. The server does not sign and does not
send — transactions is what the caller takes to their own wallet. A launch
with anything in problems has no transactions at all, and every problem is a
plain sentence naming what to fix.
Only the erc20-direct provider works today. Pons, Hookr and Orbio are
registered as providers that refuse with the exact unblock condition rather
than being absent from the registry — an absent venue reads as a missing
feature, a refusal reads as a known problem with a name.
Pons's refusal is worth reading rather than skipping. The recorded factory's
launchEnabled() is false, so launchToken reverts for every sender, and
PUTTY_FEE_SPLITTER is undeployed, so the promised fee share would be silently
zero. Putty refuses rather than sending a transaction that cannot succeed.
POST /pools/quote quotes a swap through a Putty pool.
GET /launches/fee quotes the mainnet launch fee, and the launch route
enforces that fee before it records anything. Per-account daily mainnet launches
are throttled; testnet and fork launches are exempt.
Where to look
apps/api/src/modules/launch/ ·
POST /launches ·
GET /launches/fee ·
POST /pools/quote ·
apps/api/test/modules/launch-provider.test.ts
What the Kits are not yet
Stated plainly, because the repository's build-gates.md gate ledger records
the same things, each with the command that proves it. Where a gap is a
credential rather than code, it is named as one, so nobody re-derives it:
- The Community Kit's bot template does not exist. Its API ships and is
tested;
packages/templates/haslandingandwebappand no bot. Writingpackages/templates/community-bot/closes it, and it needs no credential. - The Trust Kit's fork run has never executed. The ten contracts are green
on a local EVM (264 Foundry tests). The fork variant is wired —
anvil --fork-urlis in the script — and has never run. It needsROBINHOOD_RPC_URL. - The Launch Kit's Day 7 exit criterion is unmet. It is a launch on each venue with the putty fee live on a fork, which needs that RPC plus a live credential per venue.
- Pons, Hookr and Orbio do not launch. All three are registered providers
that refuse with their exact unblock condition. Pons needs a product decision
to adopt
PonsV2LaunchFactoryand a deployedPuttyFeeSplitter; that is not a code gap. - The Business Kit has no dashboard page. The API is the whole surface.
- The dashboard's Onchain page is still a placeholder — a 26-line
"coming soon" component with no API behind it.
/showcaseis real: a full page backed by fifteen/showcase/*operations.
Two of the five need no credential at all. The other three are waiting on
ROBINHOOD_RPC_URL, on live venue credentials, or on a product decision.
The fee numbers, and the one that is not the plan's
The rail fee is 0.3% (30 bps) of swap volume, decided in the build plan
(§11, decision 9) and hard-capped: PuttyFeeBase's constructor reverts above
30 bps, so no deployment can exceed it.
The Pons share is 0.5% (50 bps) by owner ruling, and the plan's "30% of the
project's fees" (§11-1) is deliberately not implemented. 30% is 3000 bps
and the splitter's MAX_SHARE_BPS is 50, so the contract cannot express that
decision. The cap is what makes it unreachable, and
test_DecidedPonsShareIsNotDeployable fails if the cap is ever raised past 3000
— so closing that gap has to be a deliberate act rather than a side effect of
some other change.