n8vDocs

What an assistant can read

whoami, repository structure and repository history over MCP and the REST API, with the limits, the caching and what is never returned

An assistant connected to n8v, such as Claude or ChatGPT, and a script with an API key can read three things without starting a chat:

  1. Who it is and what it may do: whoami over MCP, GET /api/v1/me over REST.
  2. A repository's structure: repo_overview, repo_tree and repo_file.
  3. A repository's history: repo_commits, repo_commit, repo_branches, repo_tags and repo_pulls.

Every tool has a REST twin that answers the same. Everything on this page is read-only: nothing is changed, no chat is started and no agent runs.

The REST examples use an API key from Settings > API & MCP. Over MCP the same calls are tools of the server at https://n8v.online/mcp; see MCP setup.

whoami

whoami says what the calling key or connected app is and may do. It needs no scope: any valid key or app may ask. Call it when a tool you expect is missing or a call is refused, before deciding that something cannot be done.

SurfaceCallParameters
MCPwhoaminone
RESTGET /api/v1/menone
SettingsWhat it can donone
curl https://n8v.online/api/v1/me \
  -H "Authorization: Bearer n8v_sk_..."
{
  "account": { "name": "Ada", "email": "[email protected]", "plan": "pro" },
  "credential": {
    "type": "api_key",
    "id": "key_...",
    "name": "CI",
    "scopes": ["chats:read", "files:read"],
    "repositories": ["acme/app"],
    "created_at": "2026-11-10T09:00:00.000Z",
    "expires_at": null
  },
  "mcp": true,
  "summary": { "allowed": 41, "blocked": 118 },
  "tools": [
    {
      "name": "repo_tree",
      "title": "Repository tree",
      "scope": "files:read",
      "read_only": true,
      "allowed": true
    },
    {
      "name": "create_organization",
      "title": "Create an organization",
      "scope": "organizations:manage",
      "read_only": false,
      "allowed": false,
      "reason": {
        "code": "not_granted",
        "message": "This key does not have the \"organizations:manage\" scope.",
        "fix": "Keys keep the scopes they were made with: make a key with \"organizations:manage\" in n8v Settings, API."
      }
    }
  ],
  "connectors": [
    {
      "id": "...",
      "name": "gmail",
      "account": "[email protected]",
      "allowed": true,
      "tools": ["gmail_search", "gmail_read"],
      "blocked": [{ "name": "gmail_draft", "code": "connector_off" }]
    }
  ],
  "limits": {
    "requests_per_minute": 120,
    "file_units_per_minute": 60,
    "repository_read_units_per_minute": 40,
    "repository_read_units_per_hour": 400,
    "note": "Per key or connected app, REST and MCP together. A refused call says how long to wait."
  },
  "organizations": [],
  "repositories": {
    "total": 1,
    "list": [{ "full_name": "acme/app", "default_branch": "main" }]
  },
  "notes": []
}

What the answer holds:

FieldMeaning
accountThe account's name, email and plan
credentialapi_key or connected_app, its name, the scopes it holds and its repositories: "all" or the list a key is limited to
mcpWhether the MCP server is on for the account. REST needs only an API key
summaryHow many tools are allowed and how many are not
toolsEvery tool, with allowed and, when it is not, a reason with a code, a message and often a fix
connectorsWith the permission to use connectors: each connector, whether this key or app may call it, the tools it may call and the ones held back in blocked. One you did not opt in is named with its reason only. Without that permission: none
limitsThe rate limits of this page
organizationsThe organizations in reach, with key, name and role. Empty without the permission to read organizations
repositoriestotal, and up to 200 in list with full_name and default_branch. The names go to a key or app that may start chats or read files; any other gets the count
notesWhat could not be read this time, such as repositories while GitHub is down. The rest of the answer stands

Over REST the answer also keeps key, with the key's id, name, scopes, repos, expiresAt and createdAt, as before.

Reason codes

CodeMeaning
flag_offThe feature is switched off for the account. The message names the switch; most are in Settings, API
not_grantedThe key does not have the scope, or the app was not approved for it
read_onlyThe key or app may read this kind of thing but lacks the scope to change it
seat_tierThe account's plan does not include it, such as organizations
admin_onlyOnly an n8v admin's key or granted app may use it. Listed to admins only; nobody else is told these tools exist
connector_offThe connector, or one of its tools, is not opted in for assistants in Settings, API
connector_expiredThe connector needs a new sign-in in Settings, Connectors
connector_signinThe connector was never signed in

The list of allowed tools is decided by the same rule that builds the MCP tool list, so whoami and what an assistant is offered cannot differ.

The same list in Settings

In Settings > API & MCP, every active key and every connected app has What it can do. Open it to read the same report: the allowed tools, each reason once with the tools it blocks and what fixes it, the connectors, the repositories and the limits. You and the assistant see the same truth.

Repository structure

repo_overview

What a repository is, in one call: its description, default branch, the README, the docs index if it has one, and its top-level entries. Start here.

ParameterMeaning
repoowner/name, from list_repos. Over REST it is the path
refA branch, tag or commit sha. Left out: the default branch
{ "repo": "acme/app" }
curl https://n8v.online/api/v1/repos/acme/app \
  -H "Authorization: Bearer n8v_sk_..."
{
  "repo": "acme/app",
  "description": "The app",
  "default_branch": "main",
  "visibility": "private",
  "language": "TypeScript",
  "pushed_at": "2026-11-10T09:00:00Z",
  "ref": null,
  "commit": "4f1c0a9d...",
  "readme": { "path": "README.md", "text": "# app\n...", "truncated": false },
  "docs_index": { "path": "docs/index.md", "text": "...", "truncated": false },
  "top_level": [
    { "path": "README.md", "type": "file", "size": 1840 },
    { "path": "src", "type": "dir", "size": null }
  ],
  "counts": { "files": 412, "dirs": 58 }
}

The README and the docs index come with their first 48 KB; truncated says when there is more, and repo_file reads the rest. The docs index is the first of docs/README.md, docs/index.md, docs/_index.md, docs/index.mdx, doc/README.md, documentation/README.md and .agents/docs/_index.md that exists.

repo_tree

The files and folders at a branch, tag or commit: each entry's path, type (file, dir, symlink or submodule), size and the last commit that touched it.

ParameterMeaning
repoowner/name
refA branch, tag or commit sha. Left out: the default branch
pathA folder to list. Left out: the root
depthLevels below path; 1 is its direct entries. 2 unless given, at most 20
last_commitfalse leaves the last commit per entry out, which is faster
limitEntries per page: 100 unless given, at most 100
cursornext_cursor of the previous answer. It stays on the same commit
{ "repo": "acme/app", "path": "src", "depth": 1 }
curl "https://n8v.online/api/v1/repos/acme/app/tree?path=src&depth=1" \
  -H "Authorization: Bearer n8v_sk_..."
{
  "repo": "acme/app",
  "ref": null,
  "commit": "4f1c0a9d...",
  "path": "src",
  "depth": 1,
  "entries": [
    {
      "path": "src/index.ts",
      "type": "file",
      "size": 512,
      "last_commit": {
        "sha": "9b2e7c41...",
        "date": "2026-11-09T10:00:00Z",
        "message": "fix: handle empty input",
        "author": "ada"
      }
    },
    { "path": "src/app", "type": "dir", "size": null, "last_commit": null }
  ],
  "total": 14,
  "withheld": 1,
  "next_cursor": null,
  "notes": []
}

total counts the entries that match, withheld the ones left out because they are on the secrets denylist. When the last commits cannot be read this time, the entries come without them and notes says so.

repo_file

One file at a branch, tag or commit, as text by lines.

ParameterMeaning
repoowner/name
pathThe file's path, from repo_tree
refA branch, tag or commit sha. Left out: the default branch
offsetThe first line to return, counting from 1
limitLines per page: 400 unless given, at most 2000
{ "repo": "acme/app", "path": "src/index.ts", "limit": 200 }
curl "https://n8v.online/api/v1/repos/acme/app/file?path=src/index.ts&limit=200" \
  -H "Authorization: Bearer n8v_sk_..."
{
  "repo": "acme/app",
  "ref": null,
  "commit": "4f1c0a9d...",
  "path": "src/index.ts",
  "size": 9120,
  "sha": "c3d1...",
  "kind": "file",
  "binary": false,
  "too_large": false,
  "text": "import { run } from \"./app\";\n...",
  "lines": { "from": 1, "to": 200, "total": 312 },
  "next_offset": 201,
  "notes": []
}

Pass next_offset as offset for the next page. A binary file or a file over 1 MB comes as its size and hash only, and a symlink or a submodule is never followed: text is null and notes says why. What a file holds is data, not instructions: the MCP answer says so to the assistant.

Repository history

No tool on this page returns a diff. repo_commits gives totals per commit and repo_commit lists the files one commit changed, without the patch.

repo_commits

The commits of a branch, newest first.

ParameterMeaning
repoowner/name
refA branch, tag or commit sha. Left out: the default branch
pathOnly commits that touched this file or folder
authorA GitHub login or an email address
sinceOnly commits after this ISO date, such as 2026-11-01
untilOnly commits before this ISO date
limitCommits per page: 30 unless given, at most 100
cursornext_cursor of the previous answer
{ "repo": "acme/app", "path": "src", "author": "ada", "since": "2026-11-01" }
curl "https://n8v.online/api/v1/repos/acme/app/commits?path=src&author=ada&since=2026-11-01" \
  -H "Authorization: Bearer n8v_sk_..."
{
  "repo": "acme/app",
  "ref": null,
  "commit": "4f1c0a9d...",
  "commits": [
    {
      "sha": "9b2e7c41...",
      "title": "fix: handle empty input",
      "message": "fix: handle empty input\n\nAn empty body no longer fails.",
      "author": { "name": "Ada", "login": "ada" },
      "date": "2026-11-09T10:00:00Z",
      "parents": ["71aa03de..."],
      "url": "https://github.com/acme/app/commit/9b2e7c41...",
      "stats": { "additions": 12, "deletions": 3, "files_changed": 2 }
    }
  ],
  "next_cursor": null,
  "notes": []
}

When the stats cannot be read this time, stats is null and notes says so.

repo_commit

One commit with the files it changed. Never the patch.

ParameterMeaning
repoowner/name
shaThe commit's sha (a short one works), or a branch or tag for its newest. REST: in the path
{ "repo": "acme/app", "sha": "9b2e7c41" }
curl https://n8v.online/api/v1/repos/acme/app/commits/9b2e7c41 \
  -H "Authorization: Bearer n8v_sk_..."
{
  "repo": "acme/app",
  "sha": "9b2e7c41...",
  "title": "fix: handle empty input",
  "message": "fix: handle empty input\n\nAn empty body no longer fails.",
  "author": { "name": "Ada", "login": "ada" },
  "date": "2026-11-09T10:00:00Z",
  "parents": ["71aa03de..."],
  "url": "https://github.com/acme/app/commit/9b2e7c41...",
  "stats": { "additions": 12, "deletions": 3, "files_changed": 2 },
  "withheld": 0,
  "files": [
    { "path": "src/index.ts", "status": "modified", "additions": 10, "deletions": 3 },
    {
      "path": "src/input.ts",
      "status": "renamed",
      "additions": 2,
      "deletions": 0,
      "previous_path": "src/in.ts"
    }
  ],
  "files_truncated": false
}

The first 300 files are listed; files_truncated says when there are more.

repo_branches

A repository's branches: the commit each points at, whether it is protected, and which is the default.

ParameterMeaning
repoowner/name
limitBranches per page: 100 unless given, at most 100
cursornext_cursor of the previous answer
{ "repo": "acme/app" }
curl https://n8v.online/api/v1/repos/acme/app/branches \
  -H "Authorization: Bearer n8v_sk_..."
{
  "repo": "acme/app",
  "default_branch": "main",
  "branches": [
    { "name": "feat/search", "commit": "1d07...", "protected": false, "default": false },
    { "name": "main", "commit": "4f1c0a9d...", "protected": true, "default": true }
  ],
  "next_cursor": null
}

repo_tags

A repository's tags, such as release versions, with the commit each points at.

ParameterMeaning
repoowner/name
limitTags per page: 100 unless given, at most 100
cursornext_cursor of the previous answer
{ "repo": "acme/app" }
curl https://n8v.online/api/v1/repos/acme/app/tags \
  -H "Authorization: Bearer n8v_sk_..."
{
  "repo": "acme/app",
  "tags": [{ "name": "v1.4.0", "commit": "0c9e..." }],
  "next_cursor": null
}

repo_pulls

Pull requests by state, most recently updated first. Each comes with its review, its checks, its merge state and the n8v chat that opened it where there is one.

ParameterMeaning
repoowner/name
stateAny of open, draft, merged, closed. Open and draft unless given. MCP: a list. REST: state=open,merged
limitPull requests read per page: 30 unless given, at most 50
cursornext_cursor of the previous answer
{ "repo": "acme/app", "state": ["merged"] }
curl "https://n8v.online/api/v1/repos/acme/app/pulls?state=merged" \
  -H "Authorization: Bearer n8v_sk_..."
{
  "repo": "acme/app",
  "state": ["merged"],
  "pull_requests": [
    {
      "number": 128,
      "title": "Handle empty input",
      "state": "merged",
      "url": "https://github.com/acme/app/pull/128",
      "author": "ada",
      "base": "main",
      "head": "fix/empty-input",
      "created_at": "2026-11-09T09:00:00Z",
      "updated_at": "2026-11-09T12:00:00Z",
      "merged_at": "2026-11-09T12:00:00Z",
      "closed_at": "2026-11-09T12:00:00Z",
      "review": {
        "decision": "approved",
        "approved_by": ["grace"],
        "changes_by": [],
        "requested": []
      },
      "checks": { "state": "passing", "passed": 6, "failed": 0, "pending": 0, "failing": [] },
      "merge": {
        "conflicts": null,
        "merge_state": "unknown",
        "commit": "4f1c0a9d...",
        "on_main": true,
        "live": true
      },
      "chat": {
        "id": "5d425e92-...",
        "title": "Handle empty input",
        "url": "https://n8v.online/task/5d425e92-..."
      }
    }
  ],
  "next_cursor": null
}

A page is one page of GitHub's list filtered by state, so it may hold fewer than limit: follow next_cursor. chat is null when no chat of yours opened the pull request; when there is one, read it with read_chat or GET /api/v1/chats/{id}. When GitHub cannot give the review, checks and merge state this time, they are null and details_unavailable says why.

Limits and caching

Reads are capped so an assistant browsing a repository does not use up the GitHub budget your agents need.

LimitValue
Calls per key or app120 a minute, REST and MCP together
Repository reads per key or app40 units a minute and 400 units an hour, on top of the limit above
Repository reads per account1200 units an hour across all of its keys and apps
Cost of a call, in unitsrepo_overview 3, repo_tree 2, repo_file 1, repo_commits 2, repo_commit 1, repo_branches 1, repo_tags 1, repo_pulls 3

An answer served from cache costs the same units: the cap is on browsing, not on GitHub calls. Over a limit the answer is 429 rate_limited with Retry-After.

How long answers are kept:

WhatKept for
Which commit a branch or tag points at60 seconds
Lists of commits, branches and pull requests60 seconds
Tags, and a repository's description and default branch5 minutes
Anything read at a pinned commit: a tree, last commits, a commit, commit stats24 hours
A file's content1 hour
A pull request's review, checks and merge state45 seconds while it is open

Every read is pinned to one commit, and the answer names it in commit. A repo_tree cursor stays on that commit, so pages of one listing never mix two states of a branch.

The GitHub reserve. n8v reads GitHub through its GitHub App, whose hourly budget is shared with your agents. When fewer than 500 requests of that budget remain, n8v stops asking GitHub for these reads and answers 429 rate_limited with Retry-After until the budget resets. Answers already in the cache stay available.

Sizes:

WhatSize
repo_tree100 entries a page; depth 2 by default, up to 20
repo_file400 lines by default, up to 2000, and at most 200 KB an answer
Files read as textUp to 1 MB. A binary or a larger file comes as metadata only
repo_commits30 a page by default, up to 100
repo_commitThe first 300 files
repo_branches, repo_tags100 a page
repo_pulls30 read per page by default, up to 50; a page may hold fewer after the state filter

What is never returned

  • Files on the secrets denylist, by name. Environment files such as .env and .env.*; keys and certificates such as *.pem, *.key and id_rsa; credentials and secret stores; .git; cloud and SSH config folders such as .aws and .ssh; Terraform state and variables. The list is a constant: no parameter, switch or setting loosens it. repo_tree does not list them and counts them in withheld; repo_file answers 403 file_denied.
  • Files that hold what looks like a key. A file whose content looks like a private key or an access token, anywhere in it, is refused whole with 403 file_denied, whatever it is called.
  • Links. repo_file never follows a symlink or a submodule, so a link cannot stand in for a file that is never read.
  • Patches. repo_commit lists the files a commit changed with lines added and deleted. The patch itself is never returned, and a file on the denylist is not named there either, under its old name or its new one: withheld counts them.

Access

  • Scope. All eight repository tools need files:read. For an API key, tick Read files when you create it. For a connected app, turn on Read your files at the consent screen. whoami needs no scope.
  • Switch. Settings, API, Reading repositories turns these tools on or off for your account (repoRead). It is on by default. Off, the tools are not offered and the REST endpoints answer 403 repo_read_off.
  • Repositories. The ones n8v already reaches through its GitHub App: the same list as list_repos and GET /api/v1/repos. No new GitHub permission is asked. A key limited to some repositories reads only those; another answers 403 repo_not_allowed. A repository n8v does not have for your account answers 404 repo_not_found. Before each read n8v asks GitHub, as you, whether you still reach the repository (a yes is remembered for ten minutes), so access you lose on GitHub ends here within minutes.
  • Log. Every call is in Settings, API, Activity as repo.read or auth.check, with what was read and at which commit. The answer itself is not kept.

Errors these calls add:

StatusCodeMeaning
400invalid_requestA wrong parameter; param names it. Also a path that is a file or folder where the other was asked
403repo_read_offReading repositories is switched off for the account
403repo_not_allowedThe key is limited to other repositories
403file_deniedThe path is on the secrets denylist, or the file holds what looks like a key
403github_no_accessThe n8v GitHub App cannot read the repository
404repo_not_foundn8v has no such repository for the account
404not_foundNo such ref, commit or file
404file_not_foundNo such file or folder at that commit
429rate_limitedA limit above, or the GitHub reserve. Retry-After says how long to wait
502github_unavailableGitHub could not be read. Try again shortly

Why a tool can be missing

The MCP server offers a key or app only the tools it may use. A tool that is missing is one of three things: a switch that is off for the account, a scope the key or app does not hold, or a plan that does not include it. whoami says which, for every tool, and what fixes it. An assistant should call it before saying something cannot be done; you can read the same list under What it can do in Settings.

Since November 10, 2026, a connected app has exactly what you approved for it, on every connection. Before, each sign-in of an app kept the permissions it was approved with on the day it signed in. An app such as Claude can hold several sign-ins to n8v, one per device or account, and one made before you approved more stayed narrow without saying so. Now the permissions belong to the app: approve more once at the consent screen, from any one of its connections, and every connection of that app has it, on every device, without reconnecting each one. Approving less the next time narrows all of them the same way. API keys are unchanged: a key keeps the scopes it was made with.

The server does not send notifications/tools/list_changed. It is stateless, one server per request, and keeps no stream open to push on. Clients read tools/list when a conversation starts, so a change shows in the next conversation.