Docs
Search docs

Start attaching a repository to GitHub

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

startGithubAttach

Starts an **attach** (ADR 0136): the repository becomes GitHub-synced with the GitHub repository its GitHub App binding names, keeping its own issues and every local commit. This only creates the attach run, in `planning`; a background worker computes the plan, which `GET …/github-attach` then previews and `POST …/github-attach/confirm` confirms. Nothing is written to GitHub or to the repository's refs here. Requires repository **admin**, from a **signed-in session** or an access token with **no workspace or repository lock** (ADR 0162; its scopes must reach repository admin): a locked token is refused with `403 forbidden` even when its owner is an admin, because the run records who asked. `expected_github_repository_id` must be the GitHub repository the binding names (it guards a re-bind between reading and clicking); a missing or different binding is `409 relationship_conflict`. A deployment that does not offer GitHub sync, or a relationship that can never be attached (`github_backup`), is `409 relationship_unavailable`; a repository already synced (`github_managed`) is `409 relationship_conflict` — detach it first. A repository whose sync was turned off or cut over (`neosource_managed`, either `authority_handoff`) can be attached again. **Idempotent**: while an attach to the same GitHub repository is in flight, this returns its `run_id` and creates nothing; one to a different repository is `409 relationship_conflict`.

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-attach' \
  -H 'Authorization: Bearer $NEOSOURCE_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"expected_github_repository_id":0}'

fetch

fetch("https://neosource.dev/api/repos/OWNER/REPO/github-attach", {
  method: "POST",
  headers: {
    Authorization: "Bearer $NEOSOURCE_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"expected_github_repository_id":0}),
});

Path parameters

ownerrequired

Repository owner or organization slug.

string

reporequired

Repository name.

string

Request bodyrequired

application/json

StartGithubAttachRequest

object

Body of `POST /api/repos/{owner}/{repo}/github-attach`.

expected_github_repository_idrequired

integerint64

The GitHub repository id the caller saw the repository bound to. The target is the repository's GitHub App binding; this guards a re-bind between rendering the panel and the click.

additional propertiesnot allowed

Responses

202The attach run (new, or the one already in flight)

application/json

StartGithubAttachResponse

object

`202` answer of `POST …/github-attach`.

run_idrequired

string

The attach run — the new one, or the one already in flight.

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: attach_publish_unconfirmed, conflict, relationship_conflict, relationship_unavailable
  • 429Rate limited — retry after the `Retry-After` header
  • 500Internal server error
  • 502Bad Gateway — one of: upstream
  • 503Service temporarily unavailable / at capacity — retry after the `Retry-After` header
  • 504Gateway timeout — the request exceeded the server's handling budget