API

Projects

Create and configure projects, source settings, and environments.

A project combines source code, build configuration, environment variables, domains, and deployment history. Create a project record first, then submit a deployment.

Local filesystem and edge administration operations require self-hosted capabilities, which every Synq install has. (Hosted-cloud scopes are switched off in Synq; see Cloud compatibility.)

Create a project

Create a project from a git repo or a local path, baking the build config into the project row. For an uploaded folder, use the source upload flow instead. Only name is required — everything else is auto-detected or defaulted.

const result = await ship.projects.create({
  name: "marketing-site",
  gitOwner: "acme",
  gitRepo: "site",
  gitBranch: "main",
  framework: "nextjs",
});
console.log(result);
curl -X POST "$SYNQ_URL/api/projects" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"marketing-site","gitOwner":"acme","gitRepo":"site","gitBranch":"main","framework":"nextjs"}'

Prop

Type

Creating a project does not deploy it

POST /api/projects only records the project and its config. Trigger the first build with the Deployments API (POST /api/deployments) or synq deploy.

Scale a stateless application on a cluster

Self-hosted projects can target a ready K3s server cluster. Saving the target requires fleet administration. Choose a registry repository for source builds; cluster nodes pull immutable release images using the saved registry credentials. This currently supports a single stateless application or worker, without persistent mounts or a Compose stack. Database replication requires storage and an engine operator.

const current = await ship.projects.getClusterWorkload("proj_123");
await ship.projects.setClusterTarget("proj_123", {
  clusterId: "cluster_123",
  config: { replicas: 3, imageRepository: "ghcr.io/acme/api" },
  expectedUpdatedAt: current.updatedAt,
  stateless: true,
});
// Deploy the project through the existing Deployments API.
// After that release is active, change its replicas without rebuilding:
const live = await ship.projects.getClusterWorkload("proj_123");
if (live.activeDeploymentId && live.activeClusterId === live.clusterId) {
  const scale = await ship.projects.scaleClusterWorkload("proj_123", {
    replicas: 4,
    expectedDeploymentId: live.activeDeploymentId,
    expectedUpdatedAt: live.updatedAt,
  });
  console.log(scale.deploymentId); // Follow the normal deployment progress stream.
}

Replica changes are configuration releases. They reuse the active image, wait for ready pods and use the existing update/rollback history. Allow capacity for the old and new releases together. status and observedAt describe the last observation; saved config describes future intent. Synq Edge currently enters through the first cluster control server. Automatic gateway failover, database operators and policy-driven autoscaling are separate delivery stages.

Update a project

The update body is a partial of the create body — send only the fields you want to change. Every field above is accepted and optional here, plus these update-only fields:

Prop

Type

const result = await ship.projects.update("proj_123", {
  buildCommand: "pnpm build",
  gitBranch: "release",
});
console.log(result);
curl -X PATCH "$SYNQ_URL/api/projects/proj_123" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"buildCommand":"pnpm build","gitBranch":"release"}'

Attach (or re-point) a GitHub repository to an existing project. When branch is omitted, the repo's default branch is used.

const result = await ship.projects.linkRepo("proj_123", {
  owner: "acme",
  repo: "site",
  branch: "main",
});
console.log(result);
curl -X POST "$SYNQ_URL/api/projects/proj_123/git/link" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"owner":"acme","repo":"site","branch":"main"}'

Prop

Type

Related git routes: POST /api/projects/:id/branch (change the deploy branch), POST /api/projects/:id/auto-deploy (toggle deploy-on-push), GET /api/projects/:id/branches, and GET /api/projects/:id/commit-status (is the deployed commit behind remote HEAD?). See the GitHub API for connecting the GitHub App itself.

Connect a custom domain

A shortcut that adds a domain to the project and marks it primary in one call. For the full domain lifecycle (DNS preview, verification, SSL), use the Domains API.

const result = await ship.projects.connectDomain("proj_123", {
  domain: "app.example.com",
});
console.log(result);
curl -X POST "$SYNQ_URL/api/projects/proj_123/connect" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domain":"app.example.com"}'

Prop

Type

Environment variables

GET   /api/projects/:id/env
PATCH /api/projects/:id/env

GET lists variables for the project with secret values masked. Writes go through a merge: only the keys you name are touched — upserts are inserted/updated, deletes are removed, and every other variable (including untouched masked secrets) is left intact.

const result = await ship.projects.mergeEnvVars("proj_123", {
  environment: "production",
  upserts: [
    {
      key: "DATABASE_URL",
      value: "postgres://…",
      isSecret: true,
    },
  ],
  deletes: ["OLD_FLAG"],
});
console.log(result);
curl -X PATCH "$SYNQ_URL/api/projects/proj_123/env" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "environment": "production",
    "upserts": [{"key":"DATABASE_URL","value":"postgres://…","isSecret":true}],
    "deletes": ["OLD_FLAG"]
  }'

Prop

Type

There is no full-replace endpoint

The old destructive PUT /:id/env was removed — it could wipe or corrupt masked secrets. Always send a diff via the merge PATCH.

Resources

GET   /api/projects/:id/resources
PATCH /api/projects/:id/resources
POST  /api/projects/:id/resources

Read or update the CPU/RAM/disk allotment (separately for production and build), sleep behavior, and listen port. POST is an alias of PATCH. All fields are optional; send only what you want to change.

const result = await ship.projects.updateResources("proj_123", {
  production: {
    cpuCores: 1,
    memoryMb: 1024,
  },
  sleepMode: "always_on",
});
console.log(result);
curl -X PATCH "$SYNQ_URL/api/projects/proj_123/resources" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"production":{"cpuCores":1,"memoryMb":1024},"sleepMode":"always_on"}'

Prop

Type

Sleep mode also has its own shortcut route: POST /api/projects/:id/sleep-mode.

Create an environment

POST /api/projects/:id/environments

Prop

Type

Folder uploads

See Source files to upload a folder or deploy generated code.

Transfer to / from cloud

POST /api/projects/:id/transfer/to-cloud         † self-hosted only
POST /api/projects/:id/transfer/to-self-hosted   † self-hosted only

Hosted-cloud only, switched off in Synq. These promote a local project to a hosted cloud, or pull a cloud project back to this instance; a Synq install has no cloud to transfer to. They return 404 on the hosted cloud itself. Both require project:admin.

const result = await ship.projects.transferToCloud("proj_123");
console.log(result);
curl -X POST "$SYNQ_URL/api/projects/proj_123/transfer/to-cloud" \
  -H "Authorization: Bearer $SYNQ_TOKEN"

Retry routing with logs

Routing repair can include SSH and certificate issuance. Use the streaming operation to follow its progress without the ordinary request timeout:

for await (const event of ship.projects.retryRoutingStream("proj_123")) {
  console.log(event.event, event.data);
}

The HTTP equivalent is POST /api/projects/:id/routing/retry/stream with an SSE response. Log events contain the server's messages. A terminal complete event carries status: "completed" or status: "failed"; a disconnected stream alone does not establish success. Cancelling the stream stops log delivery while an accepted repair continues under the project's runtime lock.

Errors

400 — validation failed

A field is missing or out of range (e.g. name empty, a cpuCores above 4, or git/link without owner/repo). The response names the offending field.

403 — insufficient permission

Your token or role lacks the route's tag — deleting needs project:admin, writes need project:write. See the permission model.

404 on a per-project route

The :id doesn't belong to your organization (or was deleted). A self-hosted-only route (marked †) also returns 404 when called on a hosted cloud instance. List your projects with GET /api/projects to get valid IDs.

Operations

Lifecycle

OperationSDKREST API
Read the project overview and summary counts.projects.getHome()GET /api/projects/home
project:list
Create a project record from explicit configuration.projects.create(input)POST /api/projects
project:write
Create or select a project during source import.projects.ensure(input)POST /api/projects/ensure
project:write
Return a page of accessible projects.projects.list(input?)GET /api/projects
project:list
Read one project and its active deployment ID.projects.get(id)GET /api/projects/:id
project:read
Update project configuration, including the server it builds on.projects.update(id, input)PATCH /api/projects/:id
project:write
Tear down the project and remove its records; inspect the returned steps.projects.remove(id, input?)DELETE /api/projects/:id
project:admin
Enable the project runtime.projects.enable(id)POST /api/projects/:id/enable
project:write
Disable the project runtime.projects.disable(id)POST /api/projects/:id/disable
project:write
Inspect resources that project deletion would affect.projects.deletionPreview(id)GET /api/projects/:id/deletion-preview
project:read

Source and deployment

OperationSDKREST API
Read the project’s detailed information.projects.getInfo(id)GET /api/projects/:id/info
project:read
Read linked Git source information.projects.getGitInfo(id)GET /api/projects/:id/git
project:read
List branches available from the linked source.projects.listBranches(id, input?)GET /api/projects/:id/branches
project:read
Link a Git repository to the project.projects.linkRepo(id, input)POST /api/projects/:id/git/link
project:write
Configure a release image as the deployment source.projects.setReleaseImageSource(id, input)PUT /api/projects/:id/release-image-source
project:write
Enable or disable automatic deployment triggers.projects.setAutoDeploy(id, input)POST /api/projects/:id/auto-deploy
project:write
Choose the domain used for the project webhook.projects.setWebhookDomain(id, input)POST /api/projects/:id/webhook-domain
project:write
Read a page of the project’s deployments.projects.listDeployments(id, input?)GET /api/projects/:id/deployments
project:deployment:list
Read the project’s current deployment session.projects.deploymentSession(id)POST /api/projects/:id/deployment-session
project:read
Clear authorized build cache for the project.projects.clearBuildCache(id)POST /api/projects/:id/clear-build
project:admin · Self-hosted
Inspect retained rollback capacity.projects.getRollbackCapacity(id)GET /api/projects/:id/rollback-capacity
project:read
Check deployment port requirements.projects.checkPorts(id)POST /api/projects/:id/port-check
project:read
Check the configured build output.projects.checkOutput(id)POST /api/projects/:id/output-check
project:read
Read actions that need a deployment or routing decision.projects.getPendingActions(id)GET /api/projects/:id/pending-actions
project:read
Compare source and deployed commit status.projects.getCommitStatus(id)GET /api/projects/:id/commit-status
project:read
Select the default Git branch.projects.setBranch(id, input)POST /api/projects/:id/branch
project:write
Scan an allowed local source directory.projects.scanLocal(input)POST /api/projects/scan
project:write · Self-hosted
Import an allowed local project.projects.importLocal(input)POST /api/projects/import
project:write · Self-hosted
List discoverable local projects.projects.listLocal()GET /api/projects/local
project:list · Self-hosted

Cluster scaling

OperationSDKREST API
Read the selected cluster, active release and observed replicas.projects.getClusterWorkload(id)GET /api/projects/:id/cluster
project:read · Self-hosted
Save a ready self-hosted cluster target and stateless workload configuration. Requires fleet administration and the current project timestamp.projects.setClusterTarget(id, input)PATCH /api/projects/:id/cluster
project:write · Self-hosted
Apply a replica count through a deployment using the active immutable image. Requires active deployment and project timestamp preconditions.projects.scaleClusterWorkload(id, input)POST /api/projects/:id/cluster/scale
project:write · Self-hosted

Cluster databases

OperationSDKREST API
List the project's operator-managed databases and saved observations. Available on self-hosted Synq.projects.listClusterDatabases(id)GET /api/projects/:id/cluster/databases
project:read · Self-hosted
Read saved database progress, or refresh the operator's instances, volumes and archive status with observe. Does not start or retry setup.projects.getClusterDatabase(id, input)POST /api/projects/:id/cluster/databases/inspect
project:read · Self-hosted
Create PostgreSQL or Redis on the project's ready server cluster with a stable request UUID. PostgreSQL can restore a completed archive into a new database. Requires fleet administration.projects.createClusterDatabase(id, input)POST /api/projects/:id/cluster/databases
project:write · Self-hosted
Apply supported resource, PostgreSQL replica or backup settings with the database's current sequence. Engine changes and Redis resharding require a separate migration.projects.updateClusterDatabase(id, input)PATCH /api/projects/:id/cluster/databases
project:write · Self-hosted
Resume a failed or interrupted database operation from its saved intent. The previous controller is fenced and accepted operator resources are inspected before reuse.projects.retryClusterDatabase(id, input)POST /api/projects/:id/cluster/databases/retry
project:write · Self-hosted
Request a PostgreSQL archive at the configured S3 destination. The operator owns backup execution; retry reuses the accepted backup request.projects.backupClusterDatabase(id, input)POST /api/projects/:id/cluster/databases/backup
project:write · Self-hosted
Stop a disconnected database with name and sequence confirmation. deleteData=false retains persistent disks; true explicitly reclaims owned storage. S3 archives are kept.projects.removeClusterDatabase(id, input)DELETE /api/projects/:id/cluster/databases
project:write · Self-hosted
Save a private connection URL in the project environment without overwriting an existing variable. Set envKey to null to disconnect. Redeploy explicitly to apply connection changes.projects.connectClusterDatabase(id, input)POST /api/projects/:id/cluster/databases/connect
project:write · Self-hosted
Subscribe to replayable database progress snapshots over SSE. Reconnection only reads saved state and never repeats a mutation.projects.streamClusterDatabaseEvents(id, options?)GET /api/projects/:id/cluster/databases/stream
project:read · Self-hosted

Environments and settings

OperationSDKREST API
List the project’s environments.projects.listEnvironments(id)GET /api/projects/:id/environments
project:read
Create an environment from the supplied configuration.projects.createEnvironment(id, input)POST /api/projects/:id/environments
project:write
Read environment variables with secrets masked.projects.listEnvVars(id, input?)GET /api/projects/:id/env
project:read
Upsert and delete named environment variables.projects.mergeEnvVars(id, input)PATCH /api/projects/:id/env
project:write
Reveal the saved values of named project variables. Needs write permission; the audit log records the names, never the values.projects.revealEnvVars(id, input)POST /api/projects/:id/env-reveal
project:write
Read resource limits and sleep settings.projects.getResources(id)GET /api/projects/:id/resources
project:read
Read the image builder, what the next deploy builds with, and whether the repository has an unused Dockerfile.projects.getBuilder(id)GET /api/projects/:id/builder
project:read
Change resource allocations.projects.updateResources(id, input)PATCH /api/projects/:id/resources
project:write
Read pinned cores, the exclusive core reservation, CPU weight and memory reservation, with the server's cores and who holds them.projects.getCpuPlacement(id)GET /api/projects/:id/cpu-placement
project:read · Self-hosted
Set or clear the CPU placement of the project or one service; applied live with docker update.projects.updateCpuPlacement(id, input)PUT /api/projects/:id/cpu-placement
project:write · Self-hosted
Set the project’s sleep behavior.projects.setSleepMode(id, input)POST /api/projects/:id/sleep-mode
project:write
Read the project's Markdown notes and the length limit.projects.getNotes(id)GET /api/projects/:id/notes
project:read
Replace the project's Markdown notes (up to 20,000 characters; null clears them). Needs write access.projects.setNotes(id, input)PUT /api/projects/:id/notes
project:write
Change deployment options.projects.setOptions(id, input)POST /api/projects/:id/options
project:write
Read whether a project clone token is configured.projects.getCloneToken(id)GET /api/projects/:id/clone-token
project:read
Set, rotate, or clear the project clone token.projects.updateCloneToken(id, input)PATCH /api/projects/:id/clone-token
project:admin
Read a generic Git source: remote, provider, branch, access method and deploy key.projects.getGitSource(id)GET /api/projects/:id/git-source
project:read
Set or edit a generic Git source (URL, branch, access method, username, host key reset).projects.setGitSource(id, input)PUT /api/projects/:id/git-source
project:write
Test access to the generic Git remote with the saved credential and list its branches.projects.checkGitSource(id)POST /api/projects/:id/git-source/check
project:write
Generate a new SSH deploy key for the generic Git source.projects.regenerateGitDeployKey(id)POST /api/projects/:id/git-source/deploy-key
project:admin
Reveal the push webhook URL and secret for GitLab, Gitea, Forgejo or Bitbucket.projects.getGitPushWebhook(id)GET /api/projects/:id/git-source/webhook
project:write
Replace the push webhook secret of a generic Git project.projects.rotateGitPushWebhookSecret(id)POST /api/projects/:id/git-source/webhook/rotate
project:admin
Read the replica count, whether the app can run more than one copy and why not, the volumes replicas would share, each running replica, the autoscale settings with the last CPU/memory reading, and recent scale events.projects.getReplicas(id)GET /api/projects/:id/replicas
project:read · Self-hosted
Set how many copies (1 to 20) the app runs on its server and/or its autoscale settings; scales the running deployment without a rebuild (new copies are health-checked before they get traffic, removed ones are drained).projects.setReplicas(id, input)PUT /api/projects/:id/replicas
project:write · Self-hosted

Volumes

OperationSDKREST API
List named volumes with mount paths, services, size on disk and backups.projects.listVolumes(id)GET /api/projects/:id/volumes
project:read · Self-hosted
Create a labelled named volume and mount it in the app or a service.projects.createVolume(id, input)POST /api/projects/:id/volumes
project:write · Self-hosted
Move a volume to a different mount path, keeping its data.projects.updateVolume(id, input)PATCH /api/projects/:id/volumes
project:write · Self-hosted
Stop mounting a volume; the data stays on the server.projects.detachVolume(id, input)POST /api/projects/:id/volumes/detach
project:write · Self-hosted
Delete a detached volume and its data, confirmed by name.projects.deleteVolume(id, input)DELETE /api/projects/:id/volumes
project:admin · Self-hosted

Routing and monitoring

OperationSDKREST API
Repair project routing and verify pending domains and HTTPS.projects.retryRouting(id)POST /api/projects/:id/routing/retry
project:write
Repair routing with live logs and a terminal success or failure event.projects.retryRoutingStream(id, options?)POST /api/projects/:id/routing/retry/stream
project:write
Read recent runtime log entries.projects.runtimeLogs(id, input?)GET /api/projects/:id/logs
project:read
Search stored runtime logs by time range (since/until), stream, level, service and text.projects.runtimeLogHistory(id, input?)GET /api/projects/:id/logs/history
project:read
List the project’s edge route rules.projects.listRouteRules(id)GET /api/projects/:id/route-rules
project:read · Self-hosted
Add an edge route rule.projects.createRouteRule(id, input)POST /api/projects/:id/route-rules
project:write · Self-hosted
Update an edge route rule using its typed input.projects.updateRouteRule(id, input)PATCH /api/projects/:id/route-rules/:ruleId
project:write · Self-hosted
Remove the specified edge route rule.projects.removeRouteRule(id, input)DELETE /api/projects/:id/route-rules/:ruleId
project:write · Self-hosted
Read open and resolved health incidents.projects.getIncidents(id)GET /api/projects/:id/incidents
project:read · Self-hosted
Attach a domain to the project.projects.connectDomain(id, input)POST /api/projects/:id/connect
project:write
Read the project’s edge configuration.projects.getEdgeConfig(id)GET /api/projects/:id/edge-config
project:read · Self-hosted
Obtain an authorized server-log stream token.projects.getServerLogStreamToken(id, input?)GET /api/projects/:id/server-logs/stream-token
project:read
Read recent request/server logs.projects.recentServerLogs(id, input?)GET /api/projects/:id/server-logs/recent
project:read
Stream project runtime log events.projects.streamRuntimeLogs(id, input?, options?)GET /api/projects/:id/logs/stream
project:read
Stream project request/server log events.projects.streamServerLogs(id, input?, options?)GET /api/projects/:id/server-logs/stream
project:read

Connections, storage, and apps

OperationSDKREST API
List the project’s outgoing resource connections.projects.listConnections(id)GET /api/projects/:id/connections
project:read
List accessible projects and apps for a service connection.projects.listConnectionCandidates(id)GET /api/projects/:id/connections/candidates
project:write
List projects consuming this project’s outputs.projects.listConnectionConsumers(id)GET /api/projects/:id/connections/consumers
project:read
Connect an output to a target environment variable.projects.createConnection(id, input)POST /api/projects/:id/connections
project:write
Apply a group of compatible resource connections.projects.connectBundle(id, input)POST /api/projects/:id/connections/bundle
project:write
Remove a resource connection.projects.removeConnection(id, input)DELETE /api/projects/:id/connections/:linkId
project:admin
Read the project’s object-storage binding.projects.getStorage(id)GET /api/projects/:id/storage
project:read
Bind a configured storage bucket.projects.bindStorage(id, input)POST /api/projects/:id/storage
project:write
Remove the storage binding.projects.unbindStorage(id)DELETE /api/projects/:id/storage
project:admin
Read the host ports a single app publishes, its routed port and the ports Synq reserves.projects.getPublishedPorts(id)GET /api/projects/:id/ports
project:read
Replace a single app's published ports after range, reserved-port and same-server clash checks, then recreate its container. A row without a host port gets a free one; a typed port that is taken answers 409 PORT_UNAVAILABLE with a suggestedPort. A failed apply changes nothing.projects.setPublishedPorts(id, input)PUT /api/projects/:id/ports
project:write
Recreate a single app's running container with its saved ports.projects.applyPublishedPorts(id)POST /api/projects/:id/ports/apply
project:write
List the files mounted read-only into a single app's container, without their contents.projects.listFileMounts(id)GET /api/projects/:id/file-mounts
project:read
Read one of a single app's mounted files (write permission required).projects.revealFileMount(id, input)POST /api/projects/:id/file-mounts/reveal
project:write
Create, edit or move a file mounted into a single app's container; contents are encrypted at rest.projects.putFileMount(id, input)PUT /api/projects/:id/file-mounts
project:write
Remove a mounted file from a single app; it disappears on the next deploy.projects.removeFileMount(id, input)DELETE /api/projects/:id/file-mounts
project:write

Cloud transfer

OperationSDKREST API
Request a Cloud transfer; fixed scopes currently require unavailable tenant mapping.projects.transferToCloud(id)POST /api/projects/:id/transfer/to-cloud
project:admin · Self-hosted
Request a transfer from Cloud; fixed-scope Cloud mapping limits apply.projects.transferToSelfHosted(id)POST /api/projects/:id/transfer/to-self-hosted
project:admin · Self-hosted

HTTP endpoints

OperationSDKREST API
Update resources (POST alias of the PATCH).HTTP onlyPOST /api/projects/:id/resources
project:write
Read edge readiness for the project.HTTP onlyGET /api/projects/:id/routing/edge-status
project:read
Answer an edge setup prompt for the project.HTTP onlyPOST /api/projects/:id/routing/ensure-edge/respond
project:write
Stream edge setup for the project.HTTP onlyPOST /api/projects/:id/routing/ensure-edge/stream
project:write

On this page