Check a wallet against a community's token
The holder gate. Reads the wallet's `balanceOf` on the token, through the same chain client the rest of putty uses, and answers with the balance and the holder census for that token. A balance above zero passes and comes back 200. A balance of zero is **403 `not_a_holder`**, not a 200 with `verified: false` — the refusal is the product here, so it is a status a gateway can act on without parsing the body. A chain read that fails is **502 `chain_unavailable`**, which is deliberately a third outcome: 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. `holders` is `null` when this chain read cannot count holders — that needs an indexed transfer log, which a single node cannot answer. A `null` census still comes back 200: the balance read is what gates the chat, and losing the census does not undo it. `"0"` means counted and none, which is a different answer from `null`.
The holder gate. Reads the wallet's balanceOf on the token, through the same chain client the rest of putty uses, and answers with the balance and the holder census for that token. A balance above zero passes and comes back 200. A balance of zero is 403 not_a_holder, not a 200 with verified: false — the refusal is the product here, so it is a status a gateway can act on without parsing the body. A chain read that fails is 502 chain_unavailable, which is deliberately a third outcome: 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. holders is null when this chain read cannot count holders — that needs an indexed transfer log, which a single node cannot answer. A null census still comes back 200: the balance read is what gates the chat, and losing the census does not undo it. "0" means counted and none, which is a different answer from null.
Authorization
bearerAuth A bearer token. The Account routes accept a Privy access token; the MCP transport accepts an sk- API key issued by POST /account/api-keys.
In: header
Request Body
application/json
The wallet, the chain, and the token the chat is gated on.
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/community/verify" \ -H "Content-Type: application/json" \ -d '{ "wallet": "string", "chain": "ethereum", "token": "string" }'{ "wallet": "string", "chain": "ethereum", "token": "string", "tokenSymbol": "string", "balanceRaw": "string", "balanceFormatted": "string", "decimals": 0, "verified": true, "holders": "string", "checkedAt": "string"}A chat platform delivers a message to the community bot POST
Accepts a Telegram or Discord delivery, verifies it against the bot's configured secret, and runs the command if the message was one. Unauthenticated by necessity — a delivery carries no session — so the platform's signature is the only credential and it is checked before the body is read for anything else. A delivery that is not a command is acknowledged with `applied: false` rather than refused, because both platforms retry a non-2xx and would otherwise redeliver it forever.
The holder count for a token GET
How many distinct wallets hold a non-zero balance of the token, on one chain. The number comes from the same `ChainReader` the holder gate uses, so a community that shows 1,284 holders here and refuses 0 wallets there is reading one source. A `409` means the reader in use cannot answer a census at all — an indexer is a different job from an `eth_call`, and putty does not guess a number it cannot back up. Supply a `ChainReader` whose `holderCount` is indexer-backed and the same route answers 200.