Docs
Search docs

Enable GitHub metadata sync for an imported repository

POST/api/repos/{owner}/{repo}/github-sync

enableGithubSync

Opts a GitHub-imported repository into metadata sync (issues, comments, labels) and enqueues the backfill. Requires repo admin and a GitHub-linked identity with a stored access token — the caller becomes the importer whose token authorizes the sync. The GitHub `owner/name` is resolved from the repo's import history; repos that were not imported from GitHub are rejected, as are repos that already carry natively-numbered issues or pull requests (the first enable reserves the numbering band and cannot renumber existing content — sync can only be enabled on freshly imported repos). An importer connected through the GitHub App must be able to reach the upstream with that token (the App installed on it); otherwise the first enable answers `403 github_app_install_required` before any row exists. Calling this on a sync that hit its failure budget (`state = error`) re-arms it: the state returns to `backfilling` and the backfill resumes from its persisted cursors. The same call (re-)arms the **git pull-back** for the resulting `neosource_readonly_mirror`: its refs follow the GitHub upstream from the next poller tick (`git_next_run_at`, `git_last_pulled_at`, `git_last_error` and `git_consecutive_failures` on `GET …/github-relationship`), and a pull parked by its own failure budget is reset here too. The first pull never discards local work: on a repository whose branches hold commits no upstream branch or tag is proven to contain (a local-only, ahead or diverged branch), or that holds an annotated tag the upstream does not have, it is refused and nothing moves, with `git_last_error` starting `github_sync_would_discard_refs`, giving the exact count and naming the refs in refname order (up to 500 or 64 KiB). To sync, fetch the repository from neosource and push each listed tip to GitHub — a branch under any branch name (`git push <github-remote> <local-oid>:refs/heads/neosource-rescue/<name>`), a tag as a tag (`git push <github-remote> <local-oid>:refs/tags/<name>`) — then call this again; or wait for the upcoming upgrade flow.

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/github-sync' \
  -H 'Authorization: Bearer $NEOSOURCE_TOKEN'

fetch

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

Path parameters

ownerrequired

Repository owner or organization slug.

string

reporequired

Repository name.

string

Responses

202Sync enabled and backfill enqueued

application/json

GithubSyncStatusResponse

object

Response body for both github-sync endpoints: the repo's metadata-sync posture plus the latest backfill counters.

full_namerequired

string

GitHub `owner/name` the metadata is synced from.

issues_donerequired

integerint32

Issues upserted by the latest backfill run. Resets on a cursor-resumed re-run; `0` when no backfill job exists yet.

issues_totalrequired

integerint32

Approximate total for the backfill progress bar — the repo's max GitHub issue number (PR numbers included, so an upper bound). `0` = unknown / no backfill job yet.

last_error

string | null

Most recent sync/backfill error, if any (also present while retries are still within the failure budget).

last_synced_at

integer | nullint64

Millis since the Unix epoch of the last successful poll/page.

moderequired

GithubRelationshipMode

string

Which forge owns an imported repository's refs and provider-shaped data. The ONLY relationship vocabulary, in the database (`github_repo_sync.mode`), in Rust, on the wire and in the SPA. The legacy `synced` / `mirror_readonly` pair it replaced was collapsed onto [`Self::NeosourceReadonlyMirror`] by `migrations/20260902120000_github_relationship_contract.sql`.

"github_managed""neosource_readonly_mirror""neosource_managed""github_backup"

staterequired

GithubSyncState

string

Lifecycle of a repo's metadata sync (`github_repo_sync.state`).

"backfilling""live""error"

Standard errors

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

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