# Set service source

`PUT https://api.instacloud.com/projects/{projectId}/services/{serviceId}/source`

**Token scope:** `account` · `org` · `project` — read-only tokens refused (not a GET).

Connect a repository to this compute service (admin+; replaces any previous source) — the tracked branch is built now and deployed into the service; every later push to it redeploys (unless autoDeploy is false, or the repo is public).

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `projectId` | path | `string` | yes |  |
| `serviceId` | path | `string` | yes |  |

## Request body (application/json)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | `"github"` | no |  |
| `owner` | `string` | yes |  |
| `repo` | `string` | yes |  |
| `installationId` | `integer` | no | GitHub App installation id; required unless public |
| `repoId` | `integer` | no | GitHub repository id; required unless public |
| `public` | `boolean` | no | a public repo, connected without an App installation: one-shot deploys, auto_deploy is always false |
| `branch` | `string` | no | the repo branch to track; default: the repo’s default branch |
| `rootDir` | `string \| null` | no | subdirectory to build from (monorepo); relative, no leading / or ..; null/omitted = repo root. It also decides which pushes deploy this service: the build context is this directory, so a push that changed nothing under it is skipped |
| `autoDeploy` | `boolean` | no | rebuild on every push to `branch` (default true; must be false or omitted for a public repo) |
| `buildCommand` | `string \| null` | no |  |
| `startCommand` | `string \| null` | no |  |
| `watchPaths` | `string[] \| null` | no | An include list of gitignore patterns, matched against paths RELATIVE TO THE REPOSITORY ROOT rather than to `rootDir` — e.g. ["apps/web/**", "packages/ui/**"]. A push deploys this service only when it changed a matching path; omit or null to fall back to the root-directory rule (a service with `rootDir` deploys when a push changed something under it, one building from the repo root deploys on every push). `!` narrows the list under git's own rule that a path cannot be re-included once an earlier pattern took its directory, so ["apps/web/**", "!**/*.md"] drops apps/web/README.md but not apps/web/docs/guide.md. Inert on a public repo, which cannot auto-deploy at all |
| `port` | `integer \| null` | no | the port the app listens on; written to the service |

## Response 200

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `source` | `object` | yes | Where a compute service’s running image comes from: an image somebody deployed (type=image, the absence of a connected source) or a connected repository (type=github). |
| `source.type` | `"image" \| "github"` | yes |  |
| `source.image` | `string \| null` | no | type=image — the image the service runs; null when nothing has been deployed yet |
| `source.id` | `string` | no |  |
| `source.owner` | `string` | no |  |
| `source.repo` | `string` | no |  |
| `source.branch` | `string` | no | the repo branch this service tracks; defaults to the repo’s default branch |
| `source.root_dir` | `string \| null` | no | subdirectory to build from (monorepo); null = repo root. Also decides which pushes deploy this service: the build context is this directory, so a push that changed nothing under it is skipped |
| `source.watch_paths` | `string[] \| null` | no | include list of gitignore patterns, matched against paths relative to the repository root (not to root_dir); a push deploys this service only when it changed a matching path. null/absent falls back to the root-directory rule: a service with root_dir deploys when a push changed something under it, one building from the repo root deploys on every push |
| `source.auto_deploy` | `boolean` | no | a push to `branch` rebuilds and redeploys; always false for a public repo (no App, no webhooks) |
| `source.public` | `boolean` | no | connected as a public repo, without a GitHub App installation |
| `source.installation_id` | `string \| null` | no |  |
| `source.repo_id` | `string \| null` | no | GitHub repository id (bigint as string) |
| `source.build_command` | `string \| null` | no | custom build command for source builds without a Dockerfile; null uses automatic detection |
| `source.start_command` | `string \| null` | no |  |
| `source.created_at` | `string` | no |  |
| `source.updated_at` | `string` | no |  |
| `source.last_build` | `object \| null` | no | the newest build of this service (null before the first) |
| `source.last_build.id` | `string` | no |  |
| `source.last_build.source_id` | `string \| null` | no | the service source that queued it; null once that source was disconnected |
| `source.last_build.project_id` | `string` | no |  |
| `source.last_build.service_id` | `string \| null` | no |  |
| `source.last_build.branch_id` | `string \| null` | no |  |
| `source.last_build.commit_sha` | `string` | no |  |
| `source.last_build.status` | `"queued" \| "fetching" \| "building" \| "deploying" \| "live" \| "failed"` | no |  |
| `source.last_build.image_ref` | `string \| null` | no |  |
| `source.last_build.error` | `string \| null` | no |  |
| `source.last_build.created_at` | `string` | no |  |
| `source.last_build.updated_at` | `string` | no |  |
| `build` | `object` | yes |  |
| `build.buildId` | `string` | yes |  |
| `build.queued` | `boolean` | yes | false when this commit already has a build (dedup) |
| `build.serviceId` | `string` | yes |  |

## Response 202

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | `string` | yes |  |
| `approvalId` | `string` | yes |  |
| `action` | `string` | yes |  |
| `actions` | `string[]` | yes |  |
| `message` | `string` | yes |  |
| `url` | `string` | yes |  |

## Response 400

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 403

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 404

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 409

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |
