API

Source files

Stage generated files or a directory, inspect the source, and deploy it.

Source sessions hold uploaded application files until deployment. The SDK accepts a directory or an in-memory file map. REST accepts a compressed archive through a short-lived upload session.

Deploy generated files

The SDK's deploy() helper combines staging, detection, project selection, and deployment submission. Use it when your application or AI workflow produces files:

const submitted = await ship.deploy({
  name: "generated-site",
  source: {
    type: "files",
    files: { "index.html": "<h1>Hello from Synq</h1>" },
  },
});

const outcome = await ship.deployment(submitted.deployment_id).wait({ timeoutMs: 120_000 });
if (!outcome.success) throw new Error(outcome.message ?? outcome.status);

File keys are relative paths; values may be strings or Uint8Array data. Use { type: "directory", path: "/absolute/path/to/site" } for a directory. With a remote client, files are packed on the calling machine. A native directory must be inside an allowed policy.sourceRoots entry.

deploy() optionUse
sourceRequired file map or absolute directory path.
nameSuggested project name.
projectIdDeploy into an existing authorized project.
environmentproduction or preview.
serverIdAuthorized target server; omit to use the configured target.
serviceIdsSelected detected services.
onStepSource-workflow progress callback.
signalAbort supported source work; a submitted deployment requires explicit cancellation.

The result contains deployment_id, project_id, and optional configDiagnostics. Submission and readiness are separate. See deployment outcomes.

Upload a source folder

For a custom uploader, open a session first:

const session = await ship.sources.open({ name: "my-site" });
console.log(session.sessionId, session.upload);
curl -X POST "$SYNQ_URL/api/projects/folder/session" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-site"}'

The response includes sessionId, expiresAt, and upload instructions:

Upload fieldUse
absoluteUrl, methodDestination and HTTP method for the archive.
headersRequired upload headers.
requiresAuthSend your bearer credential only when required.
withCredentialsWhether a browser upload needs credentials.

Send a gzip-compressed tar archive to the returned URL. Use the supplied instructions because the upload destination can differ by deployment mode. The self-hosted relay is POST /api/projects/folder/upload/:sessionId and limits uploads to 300 MB.

The SDK can perform both session creation and upload for you:

const staged = await ship.sources.stage({
  name: "my-site",
  source: { type: "directory", path: "/absolute/path/to/site" },
});
console.log(staged.sessionId, staged.expiresAt);

Directory packing excludes .git, node_modules, and .DS_Store. It enforces path, entry, and size limits and rejects escaping symlinks and special files. A system tar executable is not required by the SDK.

Inspect an uploaded source

const scan = await ship.sources.scan("session_123", { includeEnv: true });
console.log(scan.stack, scan.buildCommand, scan.configDiagnostics);
curl -X POST "$SYNQ_URL/api/projects/folder/scan/session_123" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"includeEnv":true}'

Detection returns the stack, package manager, commands, output paths, service definitions, and any configuration diagnostics. Inspect errors before submitting a deployment. Environment values are masked by default. The example requests editable values in the scan response with includeEnv: true; see Reveal environment values.

To finish a custom flow, use projects.ensure() to record the chosen configuration, then deployments.buildAccess() with projectId and uploadSessionId. The shared fields are documented in projects and deployments.

Upload an app archive

On a self-hosted instance, open the session with "archive": true and upload a .zip, .tar, .tar.gz, .tar.bz2, .tar.xz or .tar.zst file as-is instead of a packed folder. The format is detected from the file's bytes, a single wrapping top-level folder is unwrapped, and extraction rejects paths or symbolic links that would leave the archive. The same 300 MB limit applies to the upload and to the unpacked source, whatever the compression. 7z and rar archives are refused. Scan and deploy the session as above.

Import from a Git URL

A self-hosted instance can deploy any HTTPS or SSH repository (GitLab, Bitbucket, Gitea, Forgejo or your own server). Open an import with the access method, test access, then fetch the branch to scan:

curl -X POST "$SYNQ_URL/api/projects/git/session" \
  -H "Authorization: Bearer $SYNQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://gitlab.com/acme/web.git","authMode":"token","token":"'"$GITLAB_TOKEN"'"}'

curl -X POST "$SYNQ_URL/api/projects/git/session/$SESSION_ID/check" \
  -H "Authorization: Bearer $SYNQ_TOKEN" -H "Content-Type: application/json" -d '{}'

curl -X POST "$SYNQ_URL/api/projects/git/session/$SESSION_ID/fetch" \
  -H "Authorization: Bearer $SYNQ_TOKEN" -H "Content-Type: application/json" \
  -d '{"branch":"main"}'

With "authMode":"ssh" the response carries a generated publicKey: add it to the repository as a read-only deploy key before the check. The token is stored encrypted and never returned. Scan the same session id, then call projects.ensure() with gitSessionId set to it and deploy with deployments.buildAccess() without uploadSessionId, so every build clones the remote. The project's push webhook (GET /api/projects/:id/git-source/webhook) deploys pushes to its branch.

Operations

Resource methods

OperationSDKREST API
Package and stage generated files or an allowed directory.sources.stage(input, options?)Upload workflow
Open a folder upload session and return its upload instructions.sources.open(input?)POST /api/projects/folder/session
project:write
Detect build configuration from a staged source session.sources.scan(id, options?)POST /api/projects/folder/scan/:sessionId
project:write
Reveal explicitly requested source values after authorization.sources.reveal(id, input)POST /api/projects/folder/scan/:sessionId/env-reveal
project:write
Start a Git URL import (any https or SSH remote) with no credential, an access token, or a generated deploy key.sources.openGit(input)POST /api/projects/git/session
project:write · Self-hosted
Test access to the import's remote and list its branches; pins SSH host keys on first connect.sources.checkGit(id, input?)POST /api/projects/git/session/:sessionId/check
project:write · Self-hosted
Shallow-clone one branch into the import session so the source scan can detect the build.sources.fetchGit(id, input)POST /api/projects/git/session/:sessionId/fetch
project:write · Self-hosted

HTTP endpoints

OperationSDKREST API
Upload the gzipped tarball, or a .zip, .tar, .tar.gz, .tar.bz2, .tar.xz or .tar.zst app archive for an archive session (self-hosted relay, 300 MB limit).HTTP onlyPOST /api/projects/folder/upload/:sessionId
project:write · Self-hosted

On this page