Engineering

For instructions on how to authenticate to use this endpoint, see API overview.

Endpoints

GET
GET
GET
GET
GET
GET
GET
GET
GET

Retrieve engineering analytics attention pull

Open pull requests that need attention: failing CI, or stuck (open, non-draft, non-bot, older than 7 days), by the same rules as the ci_cards counts. Failing first, then newest, capped; total counts every match in the whole open backlog, however old. Cost and billable minutes can lag new CI by up to 5 minutes.

Required API key scopes

engineering_analytics:read

Query parameters

  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/attention_pull_requests
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/attention_pull_requests/

Example response

Status 200
RESPONSE
{
"items": [
{
"author": {
"handle": "string",
"display_name": "string",
"avatar_url": "string",
"is_bot": true
},
"repo": {
"provider": "string",
"owner": "string",
"name": "string"
},
"ci": {
"runs": 0,
"passing": 0,
"failing": 0,
"pending": 0,
"inconclusive": 0,
"failing_workflows": [
"string"
]
},
"push_history": [
{
"head_sha": "string",
"started_at": "2019-08-24T14:15:22Z",
"wall_seconds": 0,
"failed": true,
"pending": true
}
],
"number": 0,
"title": "string",
"state": "open",
"is_draft": true,
"created_at": "2019-08-24T14:15:22Z",
"merged_at": "2019-08-24T14:15:22Z",
"open_to_merge_seconds": 0,
"ready_to_merge_seconds": 0,
"labels": [
"string"
],
"pushes": 0,
"rerun_cycles": 0,
"estimated_cost_usd": 0,
"billable_minutes": 0
}
],
"total": 0,
"limit": 0
}
Status 400 Invalid source_id.

Retrieve engineering analytics attention pull

Open pull requests that need attention: failing CI, or stuck (open, non-draft, non-bot, older than 7 days), by the same rules as the ci_cards counts. Failing first, then newest, capped; total counts every match in the whole open backlog, however old. Cost and billable minutes can lag new CI by up to 5 minutes.

Required API key scopes

engineering_analytics:read

Query parameters

  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/attention_pull_requests
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/attention_pull_requests/

Example response

Status 200
RESPONSE
{
"items": [
{
"author": {
"handle": "string",
"display_name": "string",
"avatar_url": "string",
"is_bot": true
},
"repo": {
"provider": "string",
"owner": "string",
"name": "string"
},
"ci": {
"runs": 0,
"passing": 0,
"failing": 0,
"pending": 0,
"inconclusive": 0,
"failing_workflows": [
"string"
]
},
"push_history": [
{
"head_sha": "string",
"started_at": "2019-08-24T14:15:22Z",
"wall_seconds": 0,
"failed": true,
"pending": true
}
],
"number": 0,
"title": "string",
"state": "open",
"is_draft": true,
"created_at": "2019-08-24T14:15:22Z",
"merged_at": "2019-08-24T14:15:22Z",
"open_to_merge_seconds": 0,
"ready_to_merge_seconds": 0,
"labels": [
"string"
],
"pushes": 0,
"rerun_cycles": 0,
"estimated_cost_usd": 0,
"billable_minutes": 0
}
],
"total": 0,
"limit": 0
}
Status 400 Invalid source_id.

Retrieve engineering analytics author

Every author's friction over pull requests merged in the last 30 days, most first: red CI they did not cause, re-runs that failed again, CI waits, the wait for the first approval, merge-queue time and failed queue attempts, and rework. The score is a multiple of the typical author and never counts how much or how fast someone ships. Bots are excluded, and authors need at least 3 merged pull requests.

Required API key scopes

engineering_analytics:read

Query parameters

  • github_team
    string
  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/author_friction
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/author_friction/

Example response

Status 200
RESPONSE
{
"items": [
{
"groups": [
{
"group": "ci",
"score": 0.1
}
],
"author": "string",
"avatar_url": "string",
"score": 0.1,
"pr_count": 0,
"rank": 0,
"rank_low": 0,
"rank_high": 0,
"teams": [
"string"
]
}
],
"teams": [
{
"github_team": "string",
"median_score": 0.1,
"scored_author_count": 0
}
],
"available": true,
"window_days": 0,
"ranked_author_count": 0,
"github_team": "string",
"has_membership_data": true
}
Status 400 Invalid source_id or repo.

Retrieve engineering analytics author

Every author's friction over pull requests merged in the last 30 days, most first: red CI they did not cause, re-runs that failed again, CI waits, the wait for the first approval, merge-queue time and failed queue attempts, and rework. The score is a multiple of the typical author and never counts how much or how fast someone ships. Bots are excluded, and authors need at least 3 merged pull requests.

Required API key scopes

engineering_analytics:read

Query parameters

  • github_team
    string
  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/author_friction
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/author_friction/

Example response

Status 200
RESPONSE
{
"items": [
{
"groups": [
{
"group": "ci",
"score": 0.1
}
],
"author": "string",
"avatar_url": "string",
"score": 0.1,
"pr_count": 0,
"rank": 0,
"rank_low": 0,
"rank_high": 0,
"teams": [
"string"
]
}
],
"teams": [
{
"github_team": "string",
"median_score": 0.1,
"scored_author_count": 0
}
],
"available": true,
"window_days": 0,
"ranked_author_count": 0,
"github_team": "string",
"has_membership_data": true
}
Status 400 Invalid source_id or repo.

Retrieve engineering analytics author friction

One author's friction over pull requests merged in the last 30 days, next to the median of each of the author's teams, and the author's pull requests that added the most friction. Bots are excluded.

Required API key scopes

engineering_analytics:read

Query parameters

  • author
    string
  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/author_friction_detail
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/author_friction_detail/

Example response

Status 200
RESPONSE
{
"author": {
"groups": [
{
"group": "ci",
"score": 0.1
}
],
"author": "string",
"avatar_url": "string",
"score": 0.1,
"pr_count": 0,
"rank": 0,
"rank_low": 0,
"rank_high": 0,
"teams": [
"string"
]
},
"teams": [
{
"github_team": "string",
"median_score": 0.1,
"scored_author_count": 0
}
],
"pull_requests": [
{
"groups": [
{
"group": "ci",
"score": 0.1
}
],
"number": 0,
"repo_owner": "string",
"repo_name": "string",
"title": "string",
"score": 0.1
}
],
"available": true,
"window_days": 0,
"ranked_author_count": 0,
"has_membership_data": true,
"pr_count": 0
}
Status 400 Missing author, or invalid source_id or repo.

Retrieve engineering analytics author friction

One author's friction over pull requests merged in the last 30 days, next to the median of each of the author's teams, and the author's pull requests that added the most friction. Bots are excluded.

Required API key scopes

engineering_analytics:read

Query parameters

  • author
    string
  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/author_friction_detail
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/author_friction_detail/

Example response

Status 200
RESPONSE
{
"author": {
"groups": [
{
"group": "ci",
"score": 0.1
}
],
"author": "string",
"avatar_url": "string",
"score": 0.1,
"pr_count": 0,
"rank": 0,
"rank_low": 0,
"rank_high": 0,
"teams": [
"string"
]
},
"teams": [
{
"github_team": "string",
"median_score": 0.1,
"scored_author_count": 0
}
],
"pull_requests": [
{
"groups": [
{
"group": "ci",
"score": 0.1
}
],
"number": 0,
"repo_owner": "string",
"repo_name": "string",
"title": "string",
"score": 0.1
}
],
"available": true,
"window_days": 0,
"ranked_author_count": 0,
"has_membership_data": true,
"pr_count": 0
}
Status 400 Missing author, or invalid source_id or repo.

Retrieve engineering analytics author workflow

One author's estimated CI cost split by workflow over a window (date_from default -30d), highest spend first. Runs are attributed to the author through their pull requests (attribution is by PR number). Returns an empty list when the job-level source isn't synced.

Required API key scopes

engineering_analytics:read

Query parameters

  • author
    string
  • date_from
    string
  • date_to
    string
  • repo
    string
  • source_id
    string

Example request

GET /api/projects/:project_id/engineering_analytics/author_workflow_costs
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/author_workflow_costs/

Example response

Status 200
RESPONSE
{
"workflow_name": "string",
"billable_minutes": 0.1,
"estimated_cost_usd": 0,
"costed_jobs": 0,
"unsettled_jobs": 0,
"excluded_jobs": 0
}
Status 400 Missing author, or invalid date or source_id.

Retrieve engineering analytics author workflow

One author's estimated CI cost split by workflow over a window (date_from default -30d), highest spend first. Runs are attributed to the author through their pull requests (attribution is by PR number). Returns an empty list when the job-level source isn't synced.

Required API key scopes

engineering_analytics:read

Query parameters

  • author
    string
  • date_from
    string
  • date_to
    string
  • repo
    string
  • source_id
    string

Example request

GET /api/projects/:project_id/engineering_analytics/author_workflow_costs
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/author_workflow_costs/

Example response

Status 200
RESPONSE
{
"workflow_name": "string",
"billable_minutes": 0.1,
"estimated_cost_usd": 0,
"costed_jobs": 0,
"unsettled_jobs": 0,
"excluded_jobs": 0
}
Status 400 Missing author, or invalid date or source_id.

Retrieve engineering analytics broken

The broken-tests triage panel: live CI failures over the last 2 days grouped into distinct failures (by test id + normalized error signature) and classified by how each is behaving right now: breaking trunk, blocking the merge queue, a new failure spreading across branches, probably-resolved, flaky, or one PR's own problem: ranked with the most urgent first. A blocking_merge_queue row is a failure on a merge-queue gate branch that never hit trunk: the commit had already passed the PR's own CI, so it is the semantic conflict the queue exists to catch, and it is holding up landings. Also returns breaking_master_jobs, the default-branch jobs whose latest run is red. Reach for this to answer 'what CI failures should I care about right now'; expand a row's latest_run_id via run_failure_logs for the failing lines. Fingerprinting is pytest-only for now (jest/playwright/cargo failures aren't grouped yet), and the breaking/resolved distinction needs the job-level source synced: without it those failures fall through to flaky/pr_only rather than being misreported.

Required API key scopes

engineering_analytics:read

Query parameters

  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/broken_tests
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/broken_tests/

Example response

Status 200
RESPONSE
{
"rows": [
{
"fingerprint": "string",
"test_id": "string",
"error_signature": "string",
"job_name": "string",
"repo": "string",
"state": "breaking_master",
"first_seen": "2019-08-24T14:15:22Z",
"last_seen": "2019-08-24T14:15:22Z",
"occurrences": 0,
"branches": 0,
"master_hits": 0,
"latest_run_id": 0,
"latest_branch": "string",
"trend_24h": [
0
],
"latest_ci_engine": "github_actions"
}
],
"breaking_master_jobs": [
"string"
],
"window_days": 0,
"truncated": true,
"limit": 0
}
Status 400 Invalid source_id.

Retrieve engineering analytics broken

The broken-tests triage panel: live CI failures over the last 2 days grouped into distinct failures (by test id + normalized error signature) and classified by how each is behaving right now: breaking trunk, blocking the merge queue, a new failure spreading across branches, probably-resolved, flaky, or one PR's own problem: ranked with the most urgent first. A blocking_merge_queue row is a failure on a merge-queue gate branch that never hit trunk: the commit had already passed the PR's own CI, so it is the semantic conflict the queue exists to catch, and it is holding up landings. Also returns breaking_master_jobs, the default-branch jobs whose latest run is red. Reach for this to answer 'what CI failures should I care about right now'; expand a row's latest_run_id via run_failure_logs for the failing lines. Fingerprinting is pytest-only for now (jest/playwright/cargo failures aren't grouped yet), and the breaking/resolved distinction needs the job-level source synced: without it those failures fall through to flaky/pr_only rather than being misreported.

Required API key scopes

engineering_analytics:read

Query parameters

  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/broken_tests
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/broken_tests/

Example response

Status 200
RESPONSE
{
"rows": [
{
"fingerprint": "string",
"test_id": "string",
"error_signature": "string",
"job_name": "string",
"repo": "string",
"state": "breaking_master",
"first_seen": "2019-08-24T14:15:22Z",
"last_seen": "2019-08-24T14:15:22Z",
"occurrences": 0,
"branches": 0,
"master_hits": 0,
"latest_run_id": 0,
"latest_branch": "string",
"trend_24h": [
0
],
"latest_ci_engine": "github_actions"
}
],
"breaking_master_jobs": [
"string"
],
"window_days": 0,
"truncated": true,
"limit": 0
}
Status 400 Invalid source_id.

Retrieve engineering analytics ci signals config

Return the atomic CI Signals configuration and aggregate GitHub warehouse sync status.

Required API key scopes

engineering_analytics:read

Response


Example request

GET /api/projects/:project_id/engineering_analytics/ci-signals-config
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/ci-signals-config/

Example response

Status 200
RESPONSE
{
"configured": true,
"enabled": true,
"sync_status": "running"
}

Retrieve engineering analytics ci signals config

Return the atomic CI Signals configuration and aggregate GitHub warehouse sync status.

Required API key scopes

engineering_analytics:read

Response


Example request

GET /api/projects/:project_id/engineering_analytics/ci-signals-config
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/ci-signals-config/

Example response

Status 200
RESPONSE
{
"configured": true,
"enabled": true,
"sync_status": "running"
}

Retrieve engineering analytics ci

Headline counts for the open-PR backlog: open PRs, distinct repos, stuck PRs (open, non-draft, non-bot, older than 7 days), and PRs with failing CI. The failing-CI count rests on the head-SHA join and can lag until late CI completions settle.

Required API key scopes

engineering_analytics:read

Query parameters

  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/ci_cards
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/ci_cards/

Example response

Status 200
RESPONSE
{
"open_prs": 0,
"repos": 0,
"stuck": 0,
"failing_ci": 0
}
Status 400 Invalid source_id.

Retrieve engineering analytics ci

Headline counts for the open-PR backlog: open PRs, distinct repos, stuck PRs (open, non-draft, non-bot, older than 7 days), and PRs with failing CI. The failing-CI count rests on the head-SHA join and can lag until late CI completions settle.

Required API key scopes

engineering_analytics:read

Query parameters

  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/ci_cards
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/ci_cards/

Example response

Status 200
RESPONSE
{
"open_prs": 0,
"repos": 0,
"stuck": 0,
"failing_ci": 0
}
Status 400 Invalid source_id.

Retrieve engineering analytics ci data

When the stored CI data of a repository was last synced from its source: one time for workflow runs and one for workflow jobs. Every stored row is at least that fresh, so use these times, not the time of the request, to say how current a CI answer is. Each time is when the last completed sync started. For a repository that also syncs Depot CI, it is the older of the GitHub and the Depot CI time.

Required API key scopes

engineering_analytics:read

Query parameters

  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/ci_data_freshness
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/ci_data_freshness/

Example response

Status 200
RESPONSE
{
"runs_synced_at": "2019-08-24T14:15:22Z",
"jobs_synced_at": "2019-08-24T14:15:22Z"
}
Status 400 Missing or invalid repo or source_id.

Retrieve engineering analytics ci data

When the stored CI data of a repository was last synced from its source: one time for workflow runs and one for workflow jobs. Every stored row is at least that fresh, so use these times, not the time of the request, to say how current a CI answer is. Each time is when the last completed sync started. For a repository that also syncs Depot CI, it is the older of the GitHub and the Depot CI time.

Required API key scopes

engineering_analytics:read

Query parameters

  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/ci_data_freshness
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/ci_data_freshness/

Example response

Status 200
RESPONSE
{
"runs_synced_at": "2019-08-24T14:15:22Z",
"jobs_synced_at": "2019-08-24T14:15:22Z"
}
Status 400 Missing or invalid repo or source_id.

Retrieve engineering analytics ci failure

The thinned CI failure logs for a pull request, grouped by failed job. Resolves the PR to its workflow runs via the pull_requests association (all of the PR's pushes, not just the latest commit), then reads the Logs product joined on run_id. Returns failed jobs only (the worker fetches logs for failures); logs_available is false when CI hasn't failed, the logs aged out of the short Logs retention, or a fork PR has no run association. Each line carries its original 1-based line number in the full pre-thinning log; lines are the failure region (errors plus surrounding context, with omission markers), capped per job and overall.

Required API key scopes

engineering_analytics:read

Query parameters

  • pr_number
    integer
  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/ci_failure_logs
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/ci_failure_logs/

Example response

Status 200
RESPONSE
{
"repo": {
"provider": "string",
"owner": "string",
"name": "string"
},
"jobs": [
{
"lines": [
{
"original_line": 0,
"text": "string"
}
],
"job_id": 0,
"run_id": 0,
"conclusion": "string",
"branch": "string",
"original_total_lines": 0,
"line_count": 0,
"truncated": true,
"ci_engine": "github_actions"
}
],
"pr_number": 0,
"runs_attributed": 0,
"logs_available": true,
"truncated": true
}
Status 400 Missing pr_number/repo, or invalid repo or source_id.

Retrieve engineering analytics ci failure

The thinned CI failure logs for a pull request, grouped by failed job. Resolves the PR to its workflow runs via the pull_requests association (all of the PR's pushes, not just the latest commit), then reads the Logs product joined on run_id. Returns failed jobs only (the worker fetches logs for failures); logs_available is false when CI hasn't failed, the logs aged out of the short Logs retention, or a fork PR has no run association. Each line carries its original 1-based line number in the full pre-thinning log; lines are the failure region (errors plus surrounding context, with omission markers), capped per job and overall.

Required API key scopes

engineering_analytics:read

Query parameters

  • pr_number
    integer
  • repo
    string
  • source_id
    string

Response


Example request

GET /api/projects/:project_id/engineering_analytics/ci_failure_logs
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/engineering_analytics/ci_failure_logs/

Example response

Status 200
RESPONSE
{
"repo": {
"provider": "string",
"owner": "string",
"name": "string"
},
"jobs": [
{
"lines": [
{
"original_line": 0,
"text": "string"
}
],
"job_id": 0,
"run_id": 0,
"conclusion": "string",
"branch": "string",
"original_total_lines": 0,
"line_count": 0,
"truncated": true,
"ci_engine": "github_actions"
}
],
"pr_number": 0,
"runs_attributed": 0,
"logs_available": true,
"truncated": true
}
Status 400 Missing pr_number/repo, or invalid repo or source_id.