API Access (Team / Enterprise)¶
Everything on this site so far covers the free, self-hosted pipeline: import commit/PR data, then generate a PDF report on demand. Team and Enterprise plans add a REST/JSON API on top of the same database, so your own internal dashboards (Grafana, an internal tool, whatever you already use) can pull these metrics directly and on your own schedule, instead of regenerating and re-reading a PDF.
Like the rest of this pipeline, it's self-hosted: it runs in your own infrastructure, against your own database. Two credentials gate it -- a license key we issue you, and an API key you generate and control yourself (see "Auth" below for the difference). No data leaves your instance to make this work, and there's nothing to sign up for beyond the plan itself.
Endpoints¶
All endpoints return JSON and accept a required start/end date
range, plus optional scoping (see below).
| Endpoint | What it returns |
|---|---|
| Author activity | Per-author commits, lines added/removed, files changed, churn ratio, and active-days/week (a consistency-of-contribution signal) -- plus PR counts and average commits per PR. |
| Review health | PR/merge counts, cycle time broken into pickup (open → first review) and review (first review → merge) stages with p50/p90 percentiles, per-reviewer load, and a review-load-balance (Gini coefficient) score. |
| Repo trends | Weekly commit/PR volume and churn ratio, per repo. |
| Contributor concentration | Per-repo commit-volume concentration (Gini coefficient + top-contributor share) -- same anonymized shape as review load balance above, applied to commits. A cheap "which repo might warrant a closer look" signal, not a per-person ranking. |
| Recency skew | Per-repo decaying-ownership signal: total historical commits/contributors vs. how many of those contributors are still active in the last 90 days (window_days in the response). 0 means everyone historical is still active; approaching 1 means most have gone quiet. Same cheap-trigger family as contributor concentration. |
| Team-to-repo activity mismatch | Per-team, per-repo coverage-gap signal: for each configured team, discovers its presumed-core repo(s) the same way team scoping does, then reports the gap between roster size and how many roster members are actually active there (lookback_days in the response). Scoped differently from every other endpoint -- see "Scoping" below. |
| Contributor departure/reassignment impact | For each real team-membership change you've recorded (a separate, optional team-events log -- see "Team events" below), checks whether that person stopped contributing to a repo they were actually active on before leaving. The one endpoint that returns a named individual (their email) rather than an anonymized aggregate -- deliberately, since it's about a real personnel event. Scoped differently from every other endpoint -- see "Scoping" below. |
| Ticket-to-first-commit lead time | Hours from a ticket's creation to the earliest commit (any repo) whose message referenced that ticket's key -- a best-effort regex match on the commit message, not a Jira-verified link. Requires the allocation-tracking flow (same one Investment Allocation uses). linked_count/total_tickets shows what fraction of tickets in the period were actually referenced in a commit message at all. Not scoped by repo/org/team -- see "Scoping" below. |
| Ticket scope mismatch | Story points vs. actual lines changed for each ticket that has both, ranked by deviation from the overall median ratio -- there's no universal correct ratio, only whether a ticket is unusual relative to your own team's typical one. Not scoped by repo/org/team -- see "Scoping" below. |
| Planning signature | Per-ticket composite of lead time, post-PR-open commit churn, and change size, each bucketed low/mid/high relative to your own tickets in the period -- only two combinations are named ("high-risk": fast + unreviewed + big; "well-planned": slow + iterated + small), everything else is unlabeled. Needs a linked commit and a linked pull request to be eligible, and at least 9 eligible tickets in the period for the buckets to mean anything. Not scoped by repo/org/team -- see "Scoping" below. |
| Cycle time | The pickup/review/total percentile breakdown as its own endpoint, for dashboards that only need that slice. |
| Deployment frequency | Weekly deployment counts per repo (every git tag counts by default; scope down to a real deploy-tagging convention with a tag-pattern filter, see DORA Metrics). |
| Lead time for changes | p50/p90/avg hours from a commit to its nearest later tag. |
| Change failure rate | Percentage of deployments (tags) linked to a Jira incident via that incident's Fix Version field, exact-matched to the tag name. Requires the optional issue-processor Jira integration -- returns 0/null with jira_configured_repo_count: 0 if it isn't set up, not a misleading 0%. |
| Mean time to restore (MTTR) | p50/p90/avg hours from a linked incident's creation to its resolution. Same issue-processor dependency as change failure rate above; still-open incidents are excluded from the average but reported separately as open_excluded. |
| AI usage | Per-author and org-wide weekly trend of commits with an AI-tool Co-authored-by: trailer (the same convention GitHub itself reads to show a tool's avatar on a commit) -- a lower bound on AI-assisted work, not a precise measurement. |
| Investment allocation | Ticket/story-point distribution, allocation trend, cycle time, and logged time, per Jira-ticket category. Requires issue-processor's separate allocation-tracking flow (independent of the CFR/MTTR one above). Not scoped by repo/org/team at all -- see "Scoping" below. |
| Author distribution trend | Weekly p50/p90 (never a per-author number) of commit count, churn ratio, and active days -- the anonymized, ranking-safe alternative to a per-author leaderboard. |
| Reviewer distribution trend | Weekly p50/p90 of reviews-per-reviewer, same anonymization reasoning as above. |
| Commits-after-open distribution trend | Weekly p50/p90 of avg commits pushed after a PR was opened, per PR. |
| Executive report | On-track/at-risk/off-track status, weekly trend, and a fixed suggestion per configured KPI (8 defaults covering deployment frequency, lead time, change failure rate, MTTR, bug effort share, feature delivery, high-priority bug share, and overdue ratio -- 4 more available opt-in), plus a budget/resource snapshot reusing Investment Allocation's own data. Requires the Enterprise plan specifically -- see "Getting access" below. Not scoped by repo/org/team at all -- see "Scoping" below. |
Deployment frequency, lead time, change failure rate, and MTTR are the four canonical DORA metrics -- all four are available here. See DORA Metrics for how deployments are counted.
Scoping¶
Every endpoint accepts:
repo(repeatable) and/ororg-- limit to specific repos, same as the free CLI's--repo/--org.team-- limit to a team's repos and (where the data has a real git author identity, like commits and AI usage) filter to that team's own members, not just repos they happen to touch. Requires a team roster to be configured. PR/review data and deployment frequency can't be attributed to a team the same way (a PR author is a vendor-API username, not a git email; a tag isn't authored by anyone), so those stay repo-scoped.
repo/org and team are mutually exclusive.
Investment allocation is one exception -- it accepts none of
repo/org/team. Jira tickets have no repo relationship (allocation
tracking is purely Jira-side data), so there's nothing to scope by; it
always returns one combined view across every tracked Jira project.
Team-to-repo activity mismatch is another exception, in the
opposite direction -- it accepts no repo/org at all, and always
scans your whole org to discover each configured team's own presumed-
core repos. team is still accepted, but only to narrow the returned
rows to one team afterward, not to change what gets scanned.
Contributor departure/reassignment impact accepts no scoping params at all -- it evaluates each recorded team-membership change against that specific person's own commit history directly.
Ticket-to-first-commit lead time and ticket scope mismatch also accept no scoping params at all, same reasoning as investment allocation -- ticket data has no repo relationship.
Executive report accepts no scoping params either -- it's deliberately one org-wide view, not a drill-down tool, same as investment allocation above.
Team events¶
A separate, optional log from the team roster above: email,team,
action,date rows (action is added or removed), recording real
team-membership changes over time -- your roster (team scoping) is a
current-snapshot, this is history. Only used by the contributor
departure/reassignment endpoint. Configuring one doesn't require the
other.
Example¶
curl "https://your-instance/v1/author-activity?start=2026-01-01&end=2026-04-01&team=Platform" \
-H "Authorization: Bearer <your key>"
{
"commit_activity": [
{
"author_email": "alice@example.com",
"author_name": "Alice Anderson",
"commits": 47,
"added": 1704,
"removed": 340,
"churn_ratio": 0.17,
"active_days_per_week": 1.6
}
],
"meta": {
"start": "2026-01-01T00:00:00Z",
"end": "2026-04-01T00:00:00Z",
"team_scope": {"team": "Platform", "repo_count": 20}
}
}
Auth¶
Two separate credentials are involved, easy to conflate since both are just an env var and a header -- don't mix them up:
- License key (
GITULTRA_LICENSE_KEY) -- gates the deployment itself. We issue this to you after you get plan access (see "Getting access" below); it's not something you generate. The service checks it once at startup and refuses to boot without a valid one -- see eng-api's README for details. - API key (
API_KEY/ENG_API_KEY, sent asAuthorization: Bearer <key>) -- authenticates your own dashboards' requests to your already-running instance. You generate this yourself (see below); we never see or store it.
Every request needs Authorization: Bearer <key> using the API key
(not the license key). Once you have plan access, there's no separate
account/key request step for the API key -- only the license key comes
from us.
Manager-scoped endpoints¶
The endpoints above are org-wide, gated by your one shared API key. These four are different: they're scoped to a single manager's own team(s), and gated by that specific manager proving control of their own email address first -- see "Manager verification flow" below.
| Endpoint | What it returns |
|---|---|
| Manager contributor summary | Per-contributor commit/PR activity for one team you manage, for one period. Pass person (an email) to narrow to one team member. |
| Manager period comparison | The same team's current period vs. the immediately-preceding period of equal length, side by side -- commit activity and cycle time for both. There's no separate "period B" input; the comparison window is always computed for you. |
| Manager team trend | Weekly commit/PR volume per repo, pre-scoped to one team you manage -- the manager-authenticated equivalent of Repo Trends above. |
| Manager email-team-report | Triggers a fresh, on-demand PDF of one team's report, emailed to your own on-file address -- never any other address, even if the request tried to say otherwise. Defaults to the last 30 days if no dates are given. |
These require the team roster CSV's role/managed_teams
configuration (see Generating Reports) --
querying a team you don't manage returns a 403, not another team's
data, or "no data."
Two-header auth model¶
Every manager-scoped endpoint needs both headers on every request, not just the API key:
Authorization: Bearer <API_KEY>-- the same shared install-wide key every other endpoint on this page requires.X-Manager-Token: <token>-- a second, per-manager credential, obtained via the verification flow below. The API key alone is not sufficient for these four endpoints.
Manager verification flow¶
POST /v1/manager/request-codewith{"email": "<manager's email>"}-- always returns{"status": "sent"}, whether or not that email is actually a manager on file (this is deliberate: it stops someone from mapping out your manager roster by trying different emails and comparing responses). If it is a real manager, a 6-digit code is emailed to that address via your own configured SMTP (SMTP_HOSTetc. -- see Scheduling for the same SMTP variables), expiring in 10 minutes. Five wrong guesses locks that code out; request a fresh one to recover.POST /v1/manager/verify-codewith{"email": ..., "code": "123456"}-- returns{"token": "...", "expires_at": "..."}on success. Use that token asX-Manager-Tokenon every call to the four endpoints above for the next 24 hours; after that, verify again.
Org-level roles (Admin / Executive / Director)¶
Above team level, three org-wide roles cover the cases the manager
flow above doesn't: an executive who wants the big picture with no
team drill-down, a director who wants team-level detail across teams
they don't personally manage, and an admin who needs full access
(including individual-contributor data) for troubleshooting -- an
audited, per-person alternative to sharing the flat API key. Verified
through the exact same two-step email flow as managers above
(POST /v1/manager/request-code / POST /v1/manager/verify-code,
same X-Manager-Token header) -- a person just needs a row in
org_roles.csv instead of (or in addition to) a manager row in the
team roster CSV. See Generating Reports for
org_roles.csv's format.
The three roles are not a simple more/less hierarchy -- each has a different shape of access:
| Access | Admin | Executive | Director |
|---|---|---|---|
| Org-level reports/metrics | Yes | Yes | Yes |
| Team-level reports/metrics (any team) | Yes | No | Yes |
| Individual-contributor metrics | Yes | No | No |
| Executive Report | Yes | Yes | No |
| Endpoint | Roles | What it returns |
|---|---|---|
| Org team trend | Admin, Executive, Director | Same as Manager team trend, but org-wide when team is omitted (Admin/Executive/Director), or for any single team when team is given (Admin/Director only -- Executive gets a 403). |
| Admin contributor summary | Admin only | Same as Manager contributor summary, for any team, not just ones you manage. |
| Admin period comparison | Admin only | Same as Manager period comparison, for any team. |
| Admin email-team-report | Admin only | Same as Manager email-team-report, for any team -- still only ever emailed to your own on-file address. |
GET /v1/executive-report (see above) additionally requires an Admin
or Executive role once ORG_ROLES_PATH is configured -- on an install
that hasn't set that up, it works exactly as before (API key alone).
Updating the roster without a restart¶
Two admin-only endpoints let an Admin update team_map.csv/
org_roles.csv live, no restart required -- useful for routine roster
changes on an otherwise long-running install:
POST /v1/admin/team-mapPOST /v1/admin/org-roles
Both take a real file upload (multipart/form-data, field name
file), gated by Authorization: Bearer <API_KEY> and
X-Manager-Token from an Admin-role token. Each upload replaces the
target file's entire contents (not a patch -- include every row you
want kept) and is validated in full before anything is written: a
malformed CSV, a non-UTF-8 file, or (for org_roles.csv specifically)
an upload that would leave zero admin rows all return a 4xx and
change nothing on disk or in memory. A valid upload takes effect
immediately, for the very next request.
This requires the target file to live somewhere the eng-api
container can actually write to -- see
Getting Started (or eng-metrics-suite-pro's
docker-compose.yml) for the writable-subdirectory mount these two
endpoints need. Hand-editing either CSV directly on disk still works
exactly as before and still requires a restart to take effect -- these
two endpoints are an additional option, not a replacement.
Generating an API key¶
Any sufficiently random string works -- a UUID, a password manager's generator, whatever you already reach for. If you don't have a preferred method:
openssl rand -hex 32
or, without openssl:
python3 -c "import secrets; print(secrets.token_hex(32))"
Set the result as ENG_API_KEY in your .env (if you're running the
eng-metrics-suite-pro compose bundle) or as the API_KEY environment
variable directly (if running the container standalone) -- then send it
back as Authorization: Bearer <that value> on every request. Treat it
like any other credential: don't commit it, and rotate it (just
generate a new one and restart the container) if you suspect it's
leaked.
Getting access¶
Available on Team and Enterprise plans -- see
gitultra.com for plan details, or apply for
beta access there if you're interested. Once you have access, we issue
you a license key (GITULTRA_LICENSE_KEY, valid 90 days, renewed on
request) -- set that alongside the API key you generate yourself (see
"Auth" above) and you're running.
The executive report endpoint is Enterprise-only -- every other
endpoint on this page works on the Team plan. If your license doesn't
cover it, that one endpoint returns a 403 (everything else keeps
working); ask us for a reissue if you upgrade.
Want to query these same metrics conversationally, from Claude Desktop or Claude Code, instead of calling the API yourself? See AI Agent Access.