neosourceDocs
Search docs

Mark a draft pull request ready for review

POST/api/repos/{owner}/{repo}/pulls/{number}/ready

markPullRequestReady

Clears the draft flag so the pull request can be merged and can arm auto-merge. Idempotent: a pull request that is already ready returns 200 unchanged.

Requires authentication using a bearer token or a session cookie — see tokens and scopes.

curl

curl -X POST 'https://neosource.dev/api/repos/OWNER/REPO/pulls/NUMBER/ready' \
  -H 'Authorization: Bearer $NEOSOURCE_TOKEN'

fetch

fetch("https://neosource.dev/api/repos/OWNER/REPO/pulls/NUMBER/ready", {
  method: "POST",
  headers: {
    Authorization: "Bearer $NEOSOURCE_TOKEN",
  },
});

Path parameters

ownerrequired

Repository owner or organization slug.

string

reporequired

Repository name.

string

numberrequired

Change request number.

integerint32

Responses

200Ready for review

application/json

PullRequestResponse

object

author_display_name

string | null

author_github_login

string | null

Last-seen GitHub login for authors with a linked GitHub identity (always set for ghosts).

author_handle

string | null

Author 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_idrequired

string

author_kind
one of
  • null

  • AccountKind

    string

    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"

auto_merge_enabled_at

integer | nullint64

When auto-merge was armed (ms since epoch); set together with `auto_merge_enabled_by`.

auto_merge_enabled_by

string | null

Account 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_idrequired

string

32-byte durable change identity (hex). Survives force-push / rebase.

created_atrequired

integerint64

descriptionrequired

string

draftrequired

boolean

head_commit

string | null

40-char Git OID hex of the source branch's current tip, or `null` before the first push resolves one.

merge_commit

string | null

40-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
  • null

  • MergeMethod

    string

    How a pull request's commits land on its target branch. A repo carries two settings over this enum (`migrations/20260820120000_repo_merge_methods.sql`): the set it *permits* (`RepoRecord::allowed_merge_methods`, never empty) and the one the merge path reaches for when the request names none (`RepoRecord::default_merge_method`, always a member of that set — enforced by the settings route, which is the only place both columns are visible at once). `Default` is [`MergeMethod::Merge`] to match the column default, which is itself the pre-existing behaviour of every repo: before this setting existed, a merge always minted a two-parent commit. `Rebase` is deliberately absent rather than merely unimplemented — it replays N commits, can conflict per commit, and rewrites the SHAs the stacked-PR re-parent invariant leans on (`plans/archive/pr-merge-methods-2026-08.md` §"Not doing"). Adding it later is one `ALTER TYPE merge_method ADD VALUE` plus an arm here, which is why the stored shape is an enum array and not a pair of booleans.

    "merge""squash"

merged_at

integer | nullint64

When the merge landed (ms since epoch). Unlike `updated_at` this never moves again, so it is the field to render as "merged <when>".

merged_by

string | null

Account 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_name

string | null

merged_by_github_login

string | null

merged_by_handle

string | null

Merger 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
  • null

  • AccountKind

    string

    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"

merged_via
one of
  • null

  • MergedVia

    string

    How a pull request's merge was triggered — the companion to [`MergeMethod`], which says what the merge *minted*. The two are independent: a squash can land either by hand or by an armed auto-merge, and an imported mirror carries a trigger with no method at all.

    "manual""auto""import"

mirror_head_repo

string | null

For 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.

numberrequired

integerint32

originrequired

PrOrigin

string

Provenance of a pull request. `Native` PRs are created in neosource and are fully mutable; `GithubMirror` PRs are read-only imports of GitHub pull requests (one-directional metadata sync). Store-level single-row lookups filter to `Native` so a fork mirror — which stores GitHub head-ref names like `main`/`patch-1` — can never mask a native PR nor be advanced by a native push.

"native""github_mirror"

parent_pr_id

string | null

Parent 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_idrequired

string

repo_idrequired

string

source_branchrequired

string

source_repo_idrequired

string

Repo the source branch lives in. Equals `repo_id` for a same-repo PR; the contributor's fork for a fork PR.

statusrequired

PullRequestStatus

string

Lifecycle status of a pull request.

"open""merged""closed"

target_branchrequired

string

titlerequired

string

updated_atrequired

integerint64

Standard errors

Bodies documented once for the whole API — see standard errors.

  • 400Bad Request — one of: invalid_input
  • 401Authentication required
  • 403Forbidden — one of: forbidden
  • 404Not Found — one of: not_found
  • 409Conflict — one of: already_exists, conflict, non_fast_forward, github_sync_active
  • 423Locked — one of: busy
  • 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