putty
API ReferenceTools

Buy an AI review of a contract

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.

POST
/verify/ai-review

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 thecredits.chargedin a 200 is what the review actually cost. The ledger action isai_review, deliberately not review: CREDIT_PRICES.reviewis 0, because a deterministic security check is included in every deploy. The two must not share a row inGET /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.clearedForMainnetis stillfalsein the same response — the paid report is a second opinion, not a clearance. Withoutsource` there is nothing for a model to read, so the route is a 400 rather than a review of an empty string.

Authorization

bearerAuth
AuthorizationBearer <token>

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 same contract POST /verify takes, plus how many independent passes to run. source is required here.

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/verify/ai-review" \  -H "Content-Type: application/json" \  -d '{    "chain": "string",    "abi": [      {        "property1": null,        "property2": null      }    ]  }'
{  "report": {    "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"  },  "review": {    "model": "string",    "passes": 1,    "findings": [      {        "severity": "info",        "title": "string",        "detail": "string",        "location": "string",        "pass": 0,        "agreedBy": 0      }    ],    "summary": "string"  },  "credits": {    "charged": 0,    "action": "string",    "balance": {      "allowance": 0,      "purchased": 0,      "total": 0    }  }}

Verify a contract and get a verdict POST

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.

What every agent spent, over time GET

The caller's agent spend over a window: one row per agent, most expensive first, with the window's totals. `totalCredits` is every credit the caller spent on agent calls in the window and is equal to the sum of `agents[].credits`. A drift between those two is a bug, and it is the property this surface exists to make visible: a metering path that charged without recording (or recorded without charging) would show up here as a total that does not add up. Deregistered agents are included, under the name they had. A month-old charge belongs to the agent that made it whether or not it is still registered.