putty
API ReferenceTools

Verify a contract and get a verdict

Runs all seven checks — template match, static analysis, permission diff, hook permission read, tests + fork, invariants, plain-English readback — and answers with the report and the one label it earned. **Free, and no bearer token.** This is Verify lite from §3.7: paste a contract and get the permission diff and the readback. No credits are charged and none can be — the route never calls the credit ledger at all. **What you must send.** `chain` and `abi` are the only required fields, and they are the minimum because a report is only as honest as the evidence it was given. Omit `bytecode` and the template match **skips**; omit `source` and static analysis **skips**; omit `address` and the owner read is `unknown` and the hook read **skips**; send an empty `abi` and the permission diff **skips**. Those are the module's honest answers, not refusals: a check that could not run is reported as `skipped` with a `reason` and caps the label at `experimental`, because a check that did not run has verified nothing. **What comes back.** `label` is one of exactly three values and `labelExplanation` is the sentence that must be shown beside it — `verified custom` means every check passed and putty has NOT had the contract externally audited. `clearedForMainnet` is always `false`; the schema declares it as the literal `false` for exactly that reason. `checks` always carries seven entries in plan order, so `summary.passed + summary.failed + summary.skipped === summary.total` always holds and a check can never quietly go missing. `blockingChecks` names what to fix first.

POST
/verify

Runs all seven checks — template match, static analysis, permission diff, hook permission read, tests + fork, invariants, plain-English readback — and answers with the report and the one label it earned. Free, and no bearer token. This is Verify lite from §3.7: paste a contract and get the permission diff and the readback. No credits are charged and none can be — the route never calls the credit ledger at all. What you must send. chain and abi are the only required fields, and they are the minimum because a report is only as honest as the evidence it was given. Omit bytecode and the template match skips; omit source and static analysis skips; omit address and the owner read is unknown and the hook read skips; send an empty abi and the permission diff skips. Those are the module's honest answers, not refusals: a check that could not run is reported as skipped with a reason and caps the label at experimental, because a check that did not run has verified nothing. What comes back. label is one of exactly three values and labelExplanation is the sentence that must be shown beside it — verified custom means every check passed and putty has NOT had the contract externally audited. clearedForMainnet is always false; the schema declares it as the literal false for exactly that reason. checks always carries seven entries in plan order, so summary.passed + summary.failed + summary.skipped === summary.total always holds and a check can never quietly go missing. blockingChecks names what to fix first.

Request Body

application/json

The contract to verify. chain and abi are required; every other field unlocks a check that otherwise skips.

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

curl -X POST "https://example.com/verify" \  -H "Content-Type: application/json" \  -d '{    "chain": "string",    "abi": [      {        "property1": null,        "property2": null      }    ]  }'
{  "label": "verified template",  "labelExplanation": "string",  "checks": [    {      "id": "template_match",      "title": "string",      "blindSpot": "string",      "status": "pass",      "reason": "string",      "findings": [        {          "check": "template_match",          "severity": "error",          "rule": "string",          "message": "string",          "location": "string"        }      ],      "detail": {        "matched": {          "name": "string",          "source": "string",          "params": {            "property1": "string",            "property2": "string"          }        },        "privileged": [          {            "name": "string",            "selector": "string",            "powers": [              "mint"            ],            "holder": "string",            "unrestricted": true          }        ],        "hookPermissions": [          {            "flag": "string",            "template": "string",            "canTouch": [              "string"            ]          }        ],        "invariants": [          {            "id": "string",            "holds": true,            "detail": "string"          }        ],        "tests": {          "unitTests": {            "ran": true,            "passed": true,            "failures": [              "string"            ]          },          "fork": {            "ran": true,            "passed": true,            "observed": [              {                "behaviour": "string",                "as_described": true              }            ]          }        },        "readback": [          "string"        ]      }    }  ],  "summary": {    "total": 0,    "passed": 0,    "failed": 0,    "skipped": 0  },  "blockingChecks": [    "template_match"  ],  "clearedForTestnet": true,  "clearedForMainnet": false,  "chain": "string",  "address": "string"}

What this deployment can verify GET

The three labels and their meanings, the seven checks with their titles and stated blind spots, whether this deployment can read a contract's owner or run a project's tests and fork simulation, and how many vetted templates are compiled here. **Public, and unauthenticated on purpose.** A client reads this before it runs anything, and a page that has to log a reader in to find out what the verdict means has already lost them. `liveReads` is the field worth reading twice. A deployment with no owner read and no sandbox will report `unknown` for every privileged function and skip the tests-and-fork check, which caps the label at `experimental` however clean the contract is. `templates.compiled` decides whether `verified template` is reachable at all. Both are facts about THIS deployment, which is why they are served rather than assumed. `checks[].blindSpot` is served from the same constants the checks use, so what a client tells a user about a check's limits cannot be more generous than the check itself is.

Buy an AI review of a contract POST

Runs several independent model passes over the same source putty Verify just checked, ranks what they found by severity and by how many passes agreed, and answers with those findings **beside** the deterministic report rather than instead of it. **Authentication required, and it is the only route in this module that is.** It spends the caller's credits, so putty has to know whose ledger to write. The free checks above stay open: safety is not an upsell, and the paid review is an optional second opinion on top of a report you can already get. **Priced in credits, by size.** The cost is `ceil(sourceBytes / 1024) × 2, floored at 1, multiplied by `passes` (default 3), and it is debited BEFORE the model is called, so a caller who cannot afford it is told in a 402 and no work is started. A run that then FAILS is refunded in full, so the `credits.charged` in a 200 is what the review actually cost. The ledger action is `ai_review`, deliberately not `review`: `CREDIT_PRICES.review` is 0, because a deterministic security check is included in every deploy. The two must not share a row in `GET /billing/ledger`, or "included" and "paid for" become the same word. **It is called "AI review" and never "audit".** A model reading code is not an audit. Every finding here carries the pass it came from and how many passes agreed, and `report.clearedForMainnet` is still `false` in the same response — the paid report is a second opinion, not a clearance. Without `source` there is nothing for a model to read, so the route is a 400 rather than a review of an empty string.