# nittim — reference for AI agents **canonical: https://nittim.com/agents.md · human version: https://nittim.com/docs/api · self-review rubric: https://nittim.com/selfcheck.md** nittim audits code before it ships: deterministic scanners (committed secrets, known CVEs) plus one structured AI pass, scored, with a verdict a CI gate can act on. This file is the complete tool and endpoint reference. ## Connecting nittim speaks MCP over Streamable HTTP at one URL. A bare connection is a guest. The endpoint is `https://nittim.com/api/mcp`. Streamable HTTP is the MCP transport for servers that live at a URL: your assistant talks to nittim.com over ordinary HTTPS, and nothing runs on your machine. One command connects Claude Code: ``` claude mcp add --transport http nittim https://nittim.com/api/mcp ``` That writes one entry to your Claude Code config (`~/.claude.json`, not your repo). Undo it with `claude mcp remove nittim`. Any other MCP client takes the same URL as a config block: ``` { "nittim": { "url": "https://nittim.com/api/mcp" } } ``` Connected this way, your assistant can already call `scan_source` (committed secrets and known CVEs — no charge) and `list_modules` — no account, no key. Nothing is sent to nittim until your assistant calls a tool, and Claude Code asks you before each call. The score and the verdict need a key. ### With a key Every request that runs a paid tool authenticates with a `nittim_sk_…` key, sent as `Authorization: Bearer nittim_sk_…` — mint one on the [API keys page](/keys); the plaintext is shown once, at creation. Keep the key out of your repo: add it at user scope (pass `-s user` when you add the server, or edit `~/.claude.json`), never in a project `.mcp.json` that gets committed. A key you revoke stops working immediately. Clients that connect to remote MCP servers natively — Claude Code, claude.ai connectors, newer Claude Desktop builds — use this JSON shape: ``` { "nittim": { "url": "https://nittim.com/api/mcp", "headers": { "Authorization": "Bearer nittim_sk_…", "x-nittim-anthropic-key": "sk-ant-…" } } } ``` `x-nittim-anthropic-key` is optional — BYOK Pro subscribers only. Your key is forwarded per request and never stored. Drop it entirely to run on credits as usual. `audit_repo` doesn't take it: its report is finalized after the call ends, so it always runs on nittim's own model key and is charged in credits. Leave the header in for `audit_source` and `run_module`. Older, stdio-only clients can't dial a remote URL at all — they only speak to a local process over standard input and output. For those, `mcp-remote` is a small bridge process that runs on your machine and forwards stdio to the exact same `https://nittim.com/api/mcp` endpoint above: ``` { "nittim": { "command": "npx", "args": [ "-y", "mcp-remote", "https://nittim.com/api/mcp", "--header", "Authorization: Bearer nittim_sk_…" ] } } ``` ### Or sign in — no key to paste Rather than minting a key and putting it in a config file, you can connect and sign in to nittim the way you would to any website. Point your client at this URL instead and it will send you to nittim.com to approve the connection once: ``` claude mcp add --transport http nittim 'https://nittim.com/api/mcp?auth=oauth' ``` Any client that speaks OAuth takes the same URL as a config block, with no `Authorization` header at all — it registers itself and gets its own token: ``` { "nittim": { "url": "https://nittim.com/api/mcp?auth=oauth" } } ``` A signed-in connection is the same account a key would have reached: the same tools, the same credits, the same audit history. Keys and sign-in are interchangeable and both keep working — nothing you have configured today changes, and you can use one on one machine and the other somewhere else. A paid tool still quotes its price and waits for your Confirm; signing in is proof of who you are, never a standing permission to spend. ### Two kinds of key A key can be scoped to an organization at creation, in which case requests it authenticates carry that org's context; an unscoped key acts on your personal account. Sharing a config with a whole fleet? Mint a publishable key (`nittim_pk_…`) instead — safe to embed in a shared assistant config, it can only list modules and call the judge, spends your credits up to a per-key cap you set, and revokes just as instantly. ## The nine tools Nine ways to call 18 audit modules, one independent judge, and one opt-in loop report. ### scan_source — Scan posted files for free, no key **free** Post files straight from your assistant — no GitHub repo, no account — and get back committed secrets and known-vulnerable dependencies as hard evidence. Your assistant sends only the files it picks, and nothing is sent to nittim until it does. nittim reads them in memory and keeps the findings — path, line, a masked excerpt — so the report has a link; it never keeps the files, and never the secret itself. The only deep-look tool a guest with no key can call. ### list_modules — See every check, in plain English **free** List every check the audit engine can run, each with a plain-English description of what it looks for. Same copy as the public What we check page. A guest with no key can call this too. ### estimate_audit — Price it first — no upload required **free** Ask before you post. Give a GitHub repository URL, or a manifest of just the paths and byte sizes you would post to audit_source (no file content — nothing is uploaded to ask this), and get back the tier this selection would run at (Audit or Full Audit), how many passes, the exact price, your account's current credit balance, and whether this run would be covered by an unspent Audit. Set fullScan to price a Full Audit run instead of the default Audit — and that quote is exactly what audit_repo/audit_source will charge when you pass fullScan: true to them. A manifest with no content posted yet gets an honest upper-bound pass count, clearly marked as an estimate — computed by the SAME pricing functions audit_repo/audit_source actually charge with, so the number can't legitimately drift from the real quote. A selection too large to sell as Full Audit comes back saying so, with the way to trim it, rather than a bare refusal. Never charges, never runs an audit, never stores anything. ### audit_repo — Audit a whole repo and get the verdict **5.14 credits · report usually within 15 minutes*** Audits a GitHub repository — the deterministic scanners plus one structured AI pass, scored, with a verdict. It reads whatever commit is currently on GitHub, so push first or you're auditing stale code. The call hands the agent the audit's id straight away and the report lands usually within 15 minutes* — read it with get_audit, which returns the compact digest: verdict, scores, top findings, plus a shareable report link. Two tiers: by default it reads the repository's highest-signal source (Audit); pass fullScan: true to buy Full Audit — every eligible source file, priced by pass count, refused up front with the way to trim it if the repo is too large to read in full. It runs on nittim's own model key and is charged in credits — nittim never holds a key to your code: a private repo is read through the GitHub App you install, whose only permissions are read-only. audit_source is the tool that takes your own model key, and runs the identical pipeline on posted files. ### audit_source — Audit posted files and get the verdict **5.14 credits usually within 15 minutes* · 200 in the call on BYOK** The posted-source twin of audit_repo — same verdict, same scores, same evidence, same price — for code with nowhere to push: a builder like Abacus or Replit, or files with no GitHub remote at all. Two tiers, same as audit_repo: Audit by default, or fullScan: true for Full Audit over every eligible file you posted. On nittim's own model key it submits and hands back an id like audit_repo, landing usually within 15 minutes*. BYOK Pro is the one exception: on your own key it answers with the report inside the call instead. ### get_audit — Fetch an audit you already ran **free** Fetch an audit by its UUID at any stage. A finished one comes back as the same digest audit_repo produces; one that is still running says so and says when to ask again, so an agent never reads a pending audit as a failure and starts a second, paid one. A failed one says why, and — if you own it — what happened to the charge. Costs nothing to call, but still needs a key: reports are gated to the account that ran them. On some Full Audit runs a finding gets a second, independent read — those confirmed both times are listed first in the digest; ones caught once are listed after, still worth a look. ### run_module — Run just one check **5.03 credits per call** Run ONE module against a repo. Returns that module's findings only. Scores and a verdict come from audit_repo. ### judge_output — Get a second opinion from a different lab **5.03 credits per call** Hand over any work product — code, a plan, a claim — and an independent judge grades it: a version-pinned model from a different lab than the audit model, so an agent never has to grade its own homework. Returns severity-tagged findings and a rationale — never a score, never a verdict. ### report_loop — Share what a Nittim Loop run found — counts only **free to call · a small reward, tapering as the corpus grows** After running the Nittim Loop (nittim's free self-review rubric, run on your own model) to a real stopping point, tell nittim ANONYMISED COUNTS of what happened — pass numbers, a findings-by-category tally, a fixed count, whether each pass was clean. Never a title, a file path, a snippet, or any other description of what was found; the schema has no room for one. Ask your developer first, in plain words, and only send it if they say yes — this is data consent, not spend consent, so it never touches a balance either way. The reward is for a real loop that acted — at least two passes, something fixed, and a real stop: converged, flagged systemic, or capped after three or more passes. Reporting the same repo again from this account updates that record with the new numbers instead of being rejected — the reward is a one-time check, decided when this repo was first reported, so a repo whose first report earned nothing isn't re-checked on a later resubmission either; the response's `outcome` field says which of the two happened (`"inserted"` or `"updated"`), so a wrong `repo_hash` is caught immediately rather than days later on `/loop`. 5 credits under 20 repos reported, 3 credits under 100 repos reported, 1 credit under 500 repos reported, then nothing once the corpus reaches 500 — nittim's own report_loop answer always states today's exact number. Capped too: three rewarded repos per account, lifetime, and at most one newly-rewarded repo per account per UTC day. Results appear at [nittim.com/loop](/loop). * Most reports land within 15 minutes. Worst case, 24 hours. [See every module's key and what it checks →](/checks) ## Paying: the price is quoted, then a person clicks By default a paid run over MCP needs a click from the account owner. Nothing is charged until they press Confirm. Call a paid tool the ordinary way and it doesn't run. It answers with the price: ``` audit_repo({ repoUrl: "owner/repo" }) → This will cost 5.14 credits. AI audit for owner/repo has NOT run and NOTHING has been charged. It reads up to 214 files, across up to two passes. The report is delivered usually within 15 minutes*. { confirmRequired: true, cost: { kind: "credits", credits: 5.14, centicredits: 514 } } audit_repo({ repoUrl: "owner/repo", confirmedCost: { kind: "credits", credits: 5.14, centicredits: 514 } }) → Waiting for you to confirm. Nothing has run and nothing has been charged. Here is your link: https://nittim.com/confirm/… ← you open this and press Confirm audit_repo({ repoUrl: "owner/repo", confirmedCost: { kind: "credits", credits: 5.14, centicredits: 514 }, authorization: "a1b2c3…" }) → the audit is submitted, 5.14 credits are spent, and you get its id back. Read the report with get_audit ``` Your agent shows you that number, then calls again with `confirmedCost` set to the exact `cost` it was quoted — the whole object, both `credits` and `centicredits`, copied verbatim. If the price has changed in between (a bigger repo than the last quote, a covered audit another tab just used), the call is refused and re-quoted rather than charged the difference. Agreeing to one price is not agreeing to another. **Then you get a one-time confirm link.** That second call makes it and hands it to your agent so it can pass it on: open it, see the repo, the reach and the exact cost, and press Confirm. Your agent calls once more with the `authorization` it was given, and only then does the ledger move — by exactly that amount, once. A confirmation is good for one run, at that price, for a few minutes, and your assistant is never the one deciding — it relays the price and waits for you. (When both your MCP client and nittim's transport support in-conversation prompts, the same yes can be asked right where you are instead of through a link; today the link is the path that runs.) Runs that cost you nothing skip all of it — an org inside its included allowance, a BYOK module run. The confirmation follows the charge, not the caller. **Running unattended?** When you mint a key on [/keys](/keys) you can tick a box that lets that key spend without asking you each time — for a nightly CI audit or a long agent loop. Its spend cap still applies, the price handshake still happens, the report still lands on your account, and you can revoke it in one click. Only a signed-in person can create such a key: an API key cannot mint one, including one that is itself autonomous. **Why there are two steps and not one.** The price echo proves your assistant saw the number. It cannot prove you did — on MCP the assistant reads the result and you read what it chooses to repeat, and no wording in a tool result changes that. The click is the part a model can't do for you. `scan_source` and `list_modules` take no confirmation, ever — no key needed either, since nothing moves and nothing is charged. `get_audit` and the two deterministic modules don't cost credits, but still need a key: they read data a guest shouldn't. ## The loop Run it once, patch what it flags, run it again to see — an unchanged commit costs nothing twice. The pattern that makes this useful for an agent, not just a human with a browser: run `audit_repo` once, read back the findings and their fixes, and patch the ship-blockers it names. Run it again to see they're gone — nittim only re-audits when the commit actually moves, so an unchanged commit gets back the exact same report, at no charge. This is not a loop that spends credits every lap: `run_module` is the cheaper single-dimension check (security, privacy, secret-scan, …) for one pass along the way. To gate a whole team's pull requests instead of one agent's local loop, call the REST API below from any CI system and fail the build on the verdict — a four-step ladder, Production Ready · Production Ready with Conditions · High Risk · Not Safe for Production — so the gate can pick which step fails it. One value is not a step: Not audited, when the AI step was paused and nothing was rated. ## REST One request to start, one to read the result. In the format your tools already speak. An audit is asynchronous: kick it off, then poll. Every AI pass here — Audit and Full Audit alike — is submitted to the Batches API and lands usually within 15 minutes*; the call below hands back an id straight away, never a report. A key runs and charges on the first POST — there is no confirm step for a REST/API-key caller the way there is for an MCP client talking to a person. Start one — ``` curl -X POST https://nittim.com/api/v1/audit \ -H "Authorization: Bearer nittim_sk_…" \ -H "Content-Type: application/json" \ -d '{"repoUrl":"https://github.com/owner/repo"}' ``` No `x-nittim-anthropic-key` header on this endpoint — a batch is finalized hours later by a job that never saw your request, so a customer key sent here is refused (`400`) rather than silently run on nittim's own model key and charged as if it had. `POST /api/v1/source/audit` (below) is the BYOK door: post the repository's files instead of a GitHub URL and it answers with the report in the call, on your own key. Full Audit isn’t on offer for this repository — it holds more source than one audit reads end to end. Point nittim at a repository — or a branch — carrying less source, and this gate will say what Full Audit covers there. What’s on offer here reads the highest-risk files first. The request comes back `422` with `{ reason: "pass-ceiling" }` — refused up front, nothing charged and nothing run — never a capped, partial pass sold as Full Audit. The response is `202` with a `poll_url` and a `report_url`. Poll until the status is no longer `"running"` — ``` curl https://nittim.com/api/v1/audit/ \ -H "Authorization: Bearer nittim_sk_…" ``` Add `?format=markdown`, `?format=digest`, or `?format=sarif` to the poll URL for the full report, a compact digest, or SARIF 2.1.0 — the standard results format code-scanning tools read, so findings show up in GitHub's Security tab. ## No repo? Post the files For Abacus, Zapier, Replit and custom agents that only speak plain HTTP. Some agent platforms only speak plain HTTP — no MCP client, no GitHub repository either. If your code lives in a builder like Abacus or Replit, or your agent just has files on disk with nowhere to push them, post them straight to nittim instead of fetching a repo. Same engine, same scanners, same everything — the only difference is where the source comes from. Before posting anything, MCP's `estimate_audit` tool answers the price for a paths-and-sizes manifest — no content, nothing uploaded — so a caller with a balance too low to cover the run finds out before spending the upload, not after a `402`. At most 1000 entries; over that comes back saying so rather than a schema error. ### AI audit — score + verdict `POST /api/v1/source/audit` is the posted-source twin of `POST /api/v1/audit`: same async job, same `202` with a `poll_url`, same poll route, same credits pricing, same batch delivery on nittim's own model key. Its one difference: this is the endpoint that still honours `x-nittim-anthropic-key` (BYOK Pro) — on your own key it answers with the report in the call instead of landing usually within 15 minutes*. A single-pass AI run this way draws 2 credits from your subscription's 100-credit monthly allowance (about 50 single-pass audits a month) instead of the ordinary 5.14 credits, with your key paying the model tokens. Full Audit on this same endpoint costs more — it is priced per pass, exactly as it is on credits. Omit the header to run on credits as usual. ``` curl -X POST https://nittim.com/api/v1/source/audit \ -H "Authorization: Bearer nittim_sk_…" \ -H "Content-Type: application/json" \ -d '{ "name": "my-app", "files": [ { "path": "src/index.ts", "content": "..." }, { "path": "package.json", "content": "..." } ] }' ``` Poll the returned `poll_url` exactly as you would for a repo-based audit — it's the same `GET /api/v1/audit/` route, and every `?format=` option above works on it too. A full-coverage run past what one bounded invocation can read still batches here rather than refusing — this endpoint holds every posted byte in memory for the one request that posts it, so it can submit every pass in ONE call instead of chaining across invocations the way a fetched repository does. `422` with `{ reason: "pass-ceiling" }` survives only for source too large even at the batch lane's own wider ceiling, and `{ reason: "byok-unsupported" }` covers a customer Anthropic key over the bounded ceiling — a batch lands hours later on an invocation that never saw it, so BYOK can only ever run one-shot, inside the request that posted it. ### Free scan — evidence, no verdict `POST /api/v1/source/scan` runs the free deterministic tier — committed secrets and known-vulnerable dependencies, no model call, no charge — and answers inline, since there's no job to poll: ``` curl -X POST https://nittim.com/api/v1/source/scan \ -H "Authorization: Bearer nittim_sk_…" \ -H "Content-Type: application/json" \ -d '{ "name": "my-app", "files": [ { "path": "src/index.ts", "content": "..." } ] }' ``` The response is `200` with `{ id, share_url, report }` — `report` is hard evidence only, never a verdict. It's still an authenticated call (send the same `Authorization` header) — anonymous scanning is a web-only convenience on the dashboard, not a programmatic one. ### Both endpoints, either way `files` is the exact same shape as `audit_source`/`scan_source` take over MCP: an array of `{ path, content }`, plain text only. **Limits:** at most 500 files and ~20MB of JSON total. Pre-filter `node_modules`, build output, and binaries — they'd get dropped anyway. Post more than ~20MB of raw JSON and you get a `413` before either endpoint tries to parse it; that ceiling sits under Vercel's own request-body limit, so you get nittim's clean error instead of a platform-level failure. **Privacy:** a posted snapshot is always treated as private source — never shown in any public listing, and its report is gated to the account that posted it. A `402` from the audit endpoint means you're out of credits — the response carries a `checkoutPath`; buy more at [nittim.com/account](/account), or send an `x-nittim-anthropic-key` header (BYOK Pro) to run on your own Anthropic key instead — the audit endpoint only, not the free scan. Your key is forwarded per request and never stored. Building against an OpenAPI-importing agent builder (Abacus and similar)? Import `https://nittim.com/openapi-source.json` directly — it covers both endpoints above plus the poll route. ### Already working inside a coding assistant? Don't assemble this yourself Reading every file into your own context just to build the POST above doubles what you're holding — once as the files, again as the request — and to a security-conscious agent harness, "read files, attach a bearer credential, POST them somewhere" is indistinguishable from exfiltration, so it may refuse outright. `nittim-bridge` is a small local MCP server that does this walking, pricing and posting on the developer's behalf: it holds `NITTIM_API_KEY` and reads the repo directly from disk, so you never see the file contents or the key — only a price to relay and, later, a report id. Run `npx nittim-bridge`; call its own `estimate`/`run`/`status` tools instead of assembling the calls above by hand. Source still leaves the developer's machine exactly as it would from a direct call above — this only moves who assembles the request, never what's sent or where. ## The free Nittim Loop The free self-check your assistant runs on its own model, at zero cost — the same public categories nittim's paid audit judges. Once connected, the Nittim Loop rubric is a prompt the server hands your assistant on request — `/mcp__nittim__nittim-selfcheck` in Claude Code — so nothing is fetched from the web and nothing lands in your project. Without MCP, the same text is at `https://nittim.com/selfcheck.md`; the [Nittim Loop page](/selfcheck) shows how to install a one-line pointer to it rather than the rubric itself. ### Put it in CLAUDE.md Commit this block once and every teammate's assistant reaches for the free look on its own — no `nittim_sk_…` anywhere in the file, because the two tools it names don't take one: ``` # nittim We use nittim (nittim.com) for a scan of committed secrets and known vulnerable dependencies. What nittim does with code it reads: nittim.com/trust. It is a scan, not a verdict. It runs as an MCP server, so connecting adds one entry to this machine's client config, not to this repo, and the client still asks before a tool call. With no key it can only read — scan_source and list_modules — and no money changes hands: claude mcp add --transport http nittim https://nittim.com/api/mcp The scan works by sending file contents to nittim.com. We have read their trust page and accepted that for this repo — that call is ours, not yours. When we want that scan, ask for scan_source on the working folder. Show us the list of files you would send and wait for a yes before sending it; leave out anything holding customer data. It sends the files you pick to nittim.com and nothing else, and you can scan this folder whenever we ask. Prefer it over rereading this repo in the chat for secrets and CVEs — that burns our tokens for a worse answer. Run a paid report only when we have a nittim key and one of us has asked for a production verdict. After a nittim report, leave "are we production ready?" to us: fix the blockers it lists, and we will run nittim again. ``` ### Report what the loop found — opt-in, counts only After the loop reaches a real stopping point, `report_loop` (above) shares anonymised counts with nittim — never a title, path, or snippet — and the results show up at [nittim.com/loop](/loop), a live public read of what the loop is actually finding and fixing across every reporter. The reward is for a real loop that acted — at least two passes, something fixed, and a real stop: converged, flagged systemic, or capped after three or more passes. Report the same repo again later and it UPDATES that record with the new numbers rather than being rejected — the reward is a one-time check, decided on the first report, so it's never re-evaluated on a later resubmission even if that first report earned nothing, but the numbers stay current. 5 credits under 20 repos reported, 3 credits under 100 repos reported, 1 credit under 500 repos reported, then nothing once the corpus reaches 500 — nittim's own report_loop answer always states today's exact number. Capped too: three rewarded repos per account, lifetime, and at most one newly-rewarded repo per account per UTC day. No MCP client? The REST twin takes the identical body and returns the same shape — including `outcome: "inserted" | "updated"`, naming which of the two happened: ``` curl -X POST https://nittim.com/api/v1/loop/report \ -H "Authorization: Bearer nittim_sk_…" \ -H "Content-Type: application/json" \ -d '{ "repo_hash": "", "repo_size_bucket": "m", "passes": [ { "n": 1, "findings": { "security": { "critical": 1, "high": 0, "medium": 0, "low": 0 } }, "fixed": 0, "clean": false }, { "n": 2, "findings": {}, "fixed": 1, "clean": true } ] }' ``` ## Troubleshooting The four failures worth knowing before they cost a retry. ### The tools worked before, and now they error or look missing The server was likely updated under your connection — reconnect or reload the nittim MCP server in your client and try again. Nothing is lost, and a failed tool listing charged nothing. The server states its version in its own instructions, so a reconnected client can see that it changed. ### Every call answers 401 The key was revoked, expired, or mistyped. Mint a new `nittim_sk_…` on the [API keys page](/keys) and replace the bearer token in your MCP config — a revoked key stops working immediately, and there is no grace period to wait out. ### get_audit has said “running” for a while That is batch delivery, not a hang: the report lands usually within 15 minutes*. Keep polling `get_audit` — at least a minute apart, never in a tight loop — and never start a second audit because the first is still running. ### “Not found” on an audit id you own A report is read by the account that ran it: the key your config sends probably belongs to a different account — or an org-scoped key where a personal one ran the audit. Check which key the client is configured with, or mint one on the right account at [/keys](/keys).