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() option | Use |
|---|---|
source | Required file map or absolute directory path. |
name | Suggested project name. |
projectId | Deploy into an existing authorized project. |
environment | production or preview. |
serverId | Authorized target server; omit to use the configured target. |
serviceIds | Selected detected services. |
onStep | Source-workflow progress callback. |
signal | Abort 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 field | Use |
|---|---|
absoluteUrl, method | Destination and HTTP method for the archive. |
headers | Required upload headers. |
requiresAuth | Send your bearer credential only when required. |
withCredentials | Whether 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
| Operation | SDK | REST 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/sessionproject:write |
| Detect build configuration from a staged source session. | sources.scan(id, options?) | POST /api/projects/folder/scan/:sessionIdproject:write |
| Reveal explicitly requested source values after authorization. | sources.reveal(id, input) | POST /api/projects/folder/scan/:sessionId/env-revealproject: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/sessionproject: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/checkproject: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/fetchproject:write · Self-hosted |
HTTP endpoints
| Operation | SDK | REST 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 only | POST /api/projects/folder/upload/:sessionIdproject:write · Self-hosted |