putty
API ReferenceArtifacts

Read a token's trust receipts

Reads a token's Trust Kit setup straight from the chain and returns it as a public document: every module found, every wallet it references with its live balance, and — the part that matters — exactly what can change, who can change it, and how fast. **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 `modules` array, not a 404 — the token is real, its setup is simply empty. **Every amount is a decimal string in the token's base unit.** Divide by `10^decimals` to display one; do not parse with `Number()`, which loses precision on exactly the values a trust page exists to be precise about. **`mutable` is the load-bearing field.** A control with `mutable: false` is a Solidity `immutable`: there is no setter in the bytecode, so nothing can change it. A control with `mutable: true` always carries a `range` and a `timelockSeconds` — the projector's one invariant — so a bounded, delayed change cannot be rendered as an instant one. When a bound or a timelock could not be read from the chain, the parameter is reported as immutable and a warning is recorded, because showing an unbounded parameter as bounded is the one failure this page must not have. `provenance.warnings` is empty only when the whole document was read. A partial read is reported as partial rather than presented as complete, and a node that could not answer never becomes a silent zero.

GET
/trust/receipts/{chain}/{token}

Reads a token's Trust Kit setup straight from the chain and returns it as a public document: every module found, every wallet it references with its live balance, and — the part that matters — exactly what can change, who can change it, and how fast. 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 modules array, not a 404 — the token is real, its setup is simply empty. Every amount is a decimal string in the token's base unit. Divide by 10^decimals to display one; do not parse with Number(), which loses precision on exactly the values a trust page exists to be precise about. mutable is the load-bearing field. A control with mutable: false is a Solidity immutable: there is no setter in the bytecode, so nothing can change it. A control with mutable: true always carries a range and a timelockSeconds — the projector's one invariant — so a bounded, delayed change cannot be rendered as an instant one. When a bound or a timelock could not be read from the chain, the parameter is reported as immutable and a warning is recorded, because showing an unbounded parameter as bounded is the one failure this page must not have. provenance.warnings is empty only when the whole document was read. A partial read is reported as partial rather than presented as complete, and a node that could not answer never becomes a silent zero.

Path Parameters

chain*string

The @putty/chains slug for the chain the token is on.

Length1 <= length
token*string

The ERC-20 whose trust setup is being read.

Length1 <= length

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/trust/receipts/string/string"
{  "chain": "string",  "chainId": 1,  "token": "string",  "symbol": "string",  "decimals": 0,  "summary": {    "moduleCount": 0,    "immutableModuleCount": 0,    "mutableParameterCount": 0,    "minTimelockSeconds": 0  },  "versions": [    {      "version": 0,      "manifest": "string",      "supersededBy": "string",      "observedAt": "string",      "current": true    }  ],  "modules": [    {      "module": "fee_split",      "address": "string",      "label": "string",      "venue": "putty",      "controls": [        {          "name": "string",          "mutable": true,          "value": "string",          "unit": "bps",          "range": {            "min": "string",            "max": "string"          },          "authority": {            "address": "string",            "role": "wallet",            "proposer": "string"          },          "timelockSeconds": 0,          "pending": {            "value": "string",            "proposedAt": "string",            "executableAt": "string"          }        }      ],      "recipients": [        {          "address": "string",          "shareBps": "string",          "label": "string"        }      ],      "changeLog": [        {          "event": "string",          "txHash": "string",          "blockNumber": 0,          "timestamp": "string",          "from": "string",          "to": "string",          "actor": "string"        }      ]    }  ],  "wallets": [    {      "label": "string",      "address": "string",      "token": "string",      "balance": "string",      "role": "fee_recipient"    }  ],  "observedAt": "string",  "provenance": {    "blockNumber": 0,    "warnings": [      "string"    ]  }}

Post analytics for a connected account GET

Reads Zernio's post metrics for one connected account over a window, through the caller's own key. The account must be on the caller's Zernio profile; an account id belonging to someone else answers the same 404 as one that does not exist. Zernio's own maximum range is 366 days.

The public Receipts page GET

The same trust document the JSON route returns, rendered as a static HTML page. **No bearer token is required.** §7.1 day 4 asks for "a live Receipts page" and this is the page half of that: the JSON read is for a client, and this is the link a holder pastes into a post, a bio, or a DM. The page reads the chain at request time exactly as the JSON route does, so the two cannot disagree about what a contract says. **Every string on it is attacker-controlled and every one is escaped.** A token's `symbol()`, a module's `label`, and a split recipient's `label` are all chosen by whoever deployed the contract, and this page is reached by pasting a URL into a social post — so the page's implicit claim is "trust this document". Every such value is HTML-escaped at its interpolation, every `href` is built from a value putty itself composed, and the response carries a `Content-Security-Policy` with `script-src 'none'`, so a missed escape could not execute anything even if it got through. The page carries no script of its own and no forms. A 404 (no contract at the address) and a 422 (a contract that is not an ERC-20) are rendered as the same document shape rather than as a JSON body, so a reader who followed a bad link lands on a page that says what went wrong.