Bind a repo to a connection, creating its Project, and register its webhook (ADR-0060)
const url = 'https://example.com/v1/connections/example/repos';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"name":"example","owner":"example","register_webhook":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/connections/example/repos \ --header 'Content-Type: application/json' \ --data '{ "name": "example", "owner": "example", "register_webhook": true }'There is no projects table: a Project is a forge_repos binding
(ADR-0046), so this endpoint is the repo→Project onboarding flow for any forge
without installation-style auto-registration. After it, the repo appears on
GET /v1/repos, can hold Environments and secrets, and its pushes resolve to
a tenant. GitHub keeps binding itself from the installation webhook; this is
the Forgejo path.
Registration is attempted after the binding lands and its failure is
reported, not rolled back: the binding is the durable governance fact, a
hook is a remote side effect on a system that may be momentarily unreachable,
and unbinding on a failed hook call would delete a Project an admin just
asked for. POST …/repos/{owner}/{name}/webhook retries.
Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Connection id
Request Bodyrequired
Section titled “Request Bodyrequired”POST /v1/connections/{id}/repos body: the repo to bring under governance.
object
Also create the forge-side webhook, so a push actually reaches Scarab. Defaults to true: a bound repo with no hook is a Project that silently never builds, which is not a state anyone asks for on purpose.
Examplegenerated
{ "name": "example", "owner": "example", "register_webhook": true}Responses
Section titled “Responses”The outcome of binding a repo: the Project it created, and what happened to the webhook.
object
The governed Project’s natural key — (owner, name) in v1 (1 Project : 1
RepoRef), the same mapping installation auto-registration uses.
Why it did not, when it did not. The binding still stands — see the handler’s note on why a hook failure is reported rather than rolled back.
Did a forge-side webhook get registered (or already exist)?
Examplegenerated
{ "org": "example", "project": "example", "webhook_error": "example", "webhook_registered": true}Missing owner/name
Requires Administer on the org
No such connection, or no registry wired
The repo is already bound to a different connection