Maintainer Mode
Sign in with GitHub
Work in progress. Maintainer Mode changes every day: pages, numbers and links can change or break. Tell us what is wrong or missing.

The JSON API

All docs · The source is docs/api.md.

All endpoints are under /api/v1. GET /api/v1 lists them. Each endpoint only reads, except POST /api/v1/reports. No endpoint writes to GitHub.

The API has no compatibility layer. The project changes, and old paths are removed, not redirected.

Endpoints

EndpointReturns
GET /api/v1the list of endpoints
GET /api/v1/prsopen pull requests in one queue, paged
GET /api/v1/prs/<number>one pull request (see data.md)
GET /api/v1/people/<login>the public profile of a login, and what it maintains
GET /api/v1/needswhat waits for you, with your session only (private)
GET /api/v1/packages/<attr>one package: health, issues and pull requests
GET /api/v1/modules/<file>one NixOS module
GET /api/v1/areas/<path>one path of the nixpkgs tree
GET /api/v1/find/<name>a redirect to the package, module or area of a name
GET /api/v1/metricsthe state of nixpkgs, month by month
GET /api/v1/statusthe counts and the rules of the queues
GET /api/v1/syncwhat the server has read, and how fresh it is
GET /api/v1/eventsthe event log, as Server-Sent Events (see data.md)
GET /api/v1/archive/<owner>/<repo>/<number>one stored issue or pull request
GET /api/v1/packages/indexthe public package list of the current index (see below)
POST /api/v1/reportsuploads a strict package report (see personal.md)

Paging

A list answer has these fields:

The cursor is opaque. A list can change between two requests, so a row can show twice or not at all.

GET /api/v1/prs

ParameterMeaning
queueforyou, unowned, bumps, all (the default) or ready
sectionwith queue=ready: ready (the default), maintainer_approved, security, backports or waiting
loginwhose queue, for foryou and bumps; for foryou only, the default is your session
limitrows per page
cursorthe next_cursor of the page before

The queues:

Your subscriptions and tags are private. They count only when login is your own session. Then the answer has Cache-Control: private, no-store.

curl 'https://<host>/api/v1/prs?queue=unowned&limit=10'

GET /api/v1/people/<login>

It gives the public data of one login. It is the data of the profile page /u/<login>:

Each pull request in open_prs and recent_prs has these fields:

{
  "number": 123456,
  "title": "hello: 2.12.1 -> 2.12.2",
  "url": "https://github.com/NixOS/nixpkgs/pull/123456",
  "state": "OPEN",
  "base": "master",
  "created_at": "2026-10-01T12:00:00Z",
  "merged_at": null,
  "closed_at": null,
  "review": "no_review",
  "checks": "pass",
  "waiting_for_first_review": true
}

It never ranks people. Private data, like subscriptions and tags, is never in it.

GET /api/v1/needs

It gives the items of Needs you: what waits for you, the most urgent first. It reads your session cookie only. Without a session, the answer is 401. The answer always has Cache-Control: private, no-store. Snoozed items are not in it.

mood keeps the items of one mood: all (the default), mine, machines, review or maintain. counts always gives the count of each mood.

{
  "login": "alice",
  "mood": "all",
  "counts": { "all": 2, "mine": 1, "machines": 1, "review": 0, "maintain": 0 },
  "items": [
    {
      "key": "adv:curl",
      "cause": "advisory",
      "title": "curl: NIXPKGS-2026-1",
      "detail": "On your machines. Upstream has 8.10.0.",
      "href": "/p/curl",
      "action": "Open the package",
      "fingerprint": "3f2a9c0d1b7e4a55",
      "moods": ["machines"],
      "labels": ["critical 9.8"],
      "at": null,
      "link": "https://tracker.security.nixos.org/issue/NIXPKGS-2026-1"
    }
  ]
}

GET /api/v1/packages/index

It gives the public package list of the current nixpkgs index. The client filters a closure with it before an upload (see personal.md). It is not paged.

{
  "rev": "<index revision>",
  "attrs": { "hello": ["hello", "2.12.1"] },
  "pnames": { "hello": ["hello"] }
}

The JSON is compact. Send Accept-Encoding: gzip to get it as gzip. The ETag is the index revision, and Cache-Control is public. Send the ETag back in If-None-Match to get a 304. Before the first index, the answer is 503.

The command line

The CLI reads the same API. --server sets the host, and --json prints the raw answer.

CommandEndpoint
maintainer-mode prs --queue <queue>/api/v1/prs
maintainer-mode record <login>/api/v1/people/<login>
maintainer-mode maintains <login>/api/v1/people/<login>
maintainer-mode check <login>/api/v1/people/<login>
maintainer-mode show <name>/api/v1/find/<name>
maintainer-mode metrics/api/v1/metrics
maintainer-mode export/api/v1/packages/index
maintainer-mode submit/api/v1/packages/index and POST /api/v1/reports

prs takes --section, --login, --limit and --cursor. It prints the next cursor on stderr.

The MCP server has the same data in its tools prs and person (see actions.md).