Agent context pack for an issue
GET/api/issues/{issue_id}/context
getIssueContext
Read-only, single-call assembly of everything needed to act on an issue: the issue itself, its labels and links, its comments, and every linked pull request (open AND closed — closed PRs are prior attempts) each with a checks summary plus its review comments and review threads. Authorizes exactly like `GET /api/issues/{issue_id}` (deny folds to 404). Linked-PR details are best-effort: a PR whose lookup fails is omitted rather than failing the whole read.
Authentication is not described for this operation in the spec — that does not mean it is public. Check tokens and scopes.
curl
curl -X GET 'https://neosource.dev/api/issues/ISSUE_ID/context'fetch
fetch("https://neosource.dev/api/issues/ISSUE_ID/context", {
method: "GET",
});neo
neo issue context NUMBER OWNER/REPOPath parameters
issue_idrequired
Responses
200Issue context pack
application/json
403Forbidden — one of: forbidden, needs_scope
application/json
Standard errors
Bodies documented once for the whole API — see standard errors.
- 400Bad Request — one of: invalid_input
- 404Not Found — one of: not_found
- 429Rate limited — retry after the `Retry-After` header
- 500Internal server error
- 503Service temporarily unavailable / at capacity — retry after the `Retry-After` header
- 504Gateway timeout — the request exceeded the server's handling budget
Schemas
Referenced above. Listed here rather than expanded inline, so the same definition is not repeated at every level.
AccountKind
Rust mirror of the Pg `account_kind` enum (`accounts.kind`). `Ghost` rows are imported-author placeholders (GitHub metadata import, plans/github-metadata-import.md §6): `handle = gh-<github-id>`, `personal_workspace_id` NULL, no password, no sessions. They must never flow through `get_account*` (`AccountRow::into_record` CorruptData-errors on the NULL workspace) — author rendering goes through `PgGithubImportStore::authors_projection`, and claim-by-proof later promotes or merges them.
"human""bot""service""runner""ghost"
IssueAssigneeResponse
One assignee, resolved to something renderable. This used to be a bare `account_id` string; a UUID is not a UI, and every consumer would otherwise have to fan out to the account store itself.
account_idrequireddisplay_namerequiredhandlerequired
LabelResponse
colorrequiredcreated_atrequireddescriptionrequiredlabel_idrequirednamerequiredrepo_idteam_id
IssuePriority
Linear-style priority. Stored as the `issue_priority` Postgres enum; surfaces as a domain enum so callers never juggle magic ints.
"none""urgent""high""medium""low"
IssueStatus
"backlog""todo""in_progress""in_review""done""cancelled""duplicate"
IssueLinkKind
How one issue relates to another. Bidirectional pairs: - `Blocks` ↔ `BlockedBy` - `Related` ↔ `Related` (symmetric) - `DuplicateOf` ↔ `Duplicates` - `Tracks` ↔ `TrackedBy` `Tracks` is the one kind with a DIRECTION the product constrains beyond what this enum can express: a team ticket tracks a repo issue, never the reverse, and `TrackedBy` is only ever the inverse row the pairing writes. `IssueService::create_link` enforces both (ADR 0071 § Amendment 2026-09-05 §3) — the type permits the shape, the service refuses it.
"blocks""blocked_by""related""duplicate_of""duplicates""tracks""tracked_by"
IssueLinkTargetResponse
The link TARGET, resolved to something renderable. A link row carries two UUIDs, so the rail could only ever render `target_issue_id.slice(0, 8)` — a hex prefix is not a reference. These are the fields needed to write `ENG-9` or `acme/api#1526` and route to it. Filled by the route (only it holds the stores) and **best-effort**, the same posture as `IssuePrLinkResponse`: a lookup miss leaves the resolved fields `None` and the client falls back to the bare id, rather than the whole read failing because one repo row moved. Which resolved fields are present follows the target's own kind: `team_key`/`team_owner` for a ticket (`primary_repo_id` null), `repo_owner`/`repo_name` for a repo-bound issue. Minimal disclosure — the client branches on `primary_repo_id` anyway, so the other pair would never be rendered.
issue_idrequirednumberrequiredprimary_repo_id`None` for a team-scoped ticket — this is the field the client branches on to choose between `KEY-N` and `owner/repo#N`.
repo_namerepo_ownerRepo-bound targets only: the repo route for `owner/repo#N`.
statusrequiredteam_keyTicket targets only: `KEY` in `KEY-N`.
team_ownerTicket targets only: the workspace slug the ticket's route needs (`/{owner}/teams/{key}/issues/{n}`). Without it the ref can only be inert text.
titlerequired
ChecksSummary
Roll-up of the check states for the merge box. Counted over the newest-run-per-context set (`neosource_forge::head_checks`), so a run that a re-run superseded is not a second check.
failedrequired`failure` + `error` states — both render red and both block.
pendingrequiredskippedChecks whose job never ran (gated off by a job-level `if:`, or a `needs:` predecessor did not succeed). Counted separately from `success`; `success + failed + pending + skipped == total`.
successrequiredChecks that ran and passed. Deliberately EXCLUDES skipped ones: a skipped check carries `state: success` so it cannot block a merge, but counting it as "passed" would tell the reader that work happened when it did not — the same claim the per-check `skipped` flag exists to stop the UI making.
totalrequired
PullRequestResponse
author_display_nameauthor_github_loginLast-seen GitHub login for authors with a linked GitHub identity (always set for ghosts).
author_handleAuthor fields resolved at read time (route-layer enrichment via the ghost-safe authors projection — plans/github-metadata-import.md §7), exactly as on [`crate::IssueResponse`]. Filled on every READ — list, detail, and both context packs. `None` on a mutation receipt (the caller already has the row it just wrote) or when the account row is gone.
author_idrequiredauthor_kind- one of
auto_merge_enabled_atWhen auto-merge was armed (ms since epoch); set together with `auto_merge_enabled_by`.
auto_merge_enabled_byAccount id that armed auto-merge (merge-when-green); `null` = off. On an OPEN PR this means "will merge, as this account, the moment the merge gate reports mergeable". Cleared automatically when the head advances (new commits disarm).
change_idrequired32-byte durable change identity (hex). Survives force-push / rebase.
created_atrequireddescriptionrequireddraftrequiredhead_commit40-char Git OID hex of the source branch's current tip, or `null` before the first push resolves one.
merge_commit40-char Git OID hex of the commit the merge minted, or `null` when it minted none: an import, or a merge whose source was already reachable from the target.
merge_method- one of
merged_atWhen the merge landed (ms since epoch). Unlike `updated_at` this never moves again, so it is the field to render as "merged <when>".
merged_byAccount credited with the merge, `null` until the PR merges — and also on a merged GitHub mirror, whose upstream merger the import payload does not carry. For a merge made with a service token this is the token's OWNER; the token itself is recorded in the operation log, not here.
merged_by_display_namemerged_by_github_loginmerged_by_handleMerger fields resolved at read time, exactly as the `author_*` group above and through the same batched projection call. `None` on a mutation receipt, on an unmerged PR, and when the account row is gone.
merged_by_kind- one of
merged_via- one of
mirror_head_repoFor a fork mirror, the GitHub `owner/name` of the fork the head lives in — a non-null value means "no local branch" (fork diff renders off the pinned OID). `null` for native PRs and same-repo mirrors.
numberrequiredoriginrequiredparent_pr_idParent PR id when this PR is part of a stack (its `target_branch` matches another open PR's `source_branch`). `None` for a non-stacked PR or for the bottom of a stack.
pr_idrequiredrepo_idrequiredsource_branchrequiredsource_repo_idrequiredRepo the source branch lives in. Equals `repo_id` for a same-repo PR; the contributor's fork for a fork PR.
statusrequiredtarget_branchrequiredtitlerequiredupdated_atrequired
CommentResponse
author_display_nameauthor_github_loginLast-seen GitHub login for authors with a linked GitHub identity (always set for ghosts).
author_handleAuthor handle resolved at read time (route-layer enrichment via the ghost-safe authors projection); `None` if the account row could not be resolved.
author_idrequiredauthor_kind- one of
bodyrequiredcomment_idrequiredcreated_atrequireddeleted_ateditedrequiredparent_idupdated_atrequired
PrReviewThreadResponse
anchor_commitrequired40-char Git OID hex of the commit the line is anchored at.
anchor_linerequiredanchor_pathrequiredanchor_siderequiredcreated_atrequiredcreated_byrequiredoutdated_atpr_idrequiredresolved_atresolved_bythread_idrequiredupdated_atrequired