Sycord Deployer API
v0.9.5 · integration.json · spec.json · All responses are JSON
SYTE_API_KEY in env — never in the browser.Your database columns
| syte_uuid | required From project_connect → response.uuid |
|---|---|
| syte_domain | From response.project.domain e.g. myapp.sycord.site |
| syte_url | From response.project.url — show this link to users when live |
| deploy_status | Mirror created → deploying → running |
| preview_url | Optional cache from preview_start / preview_status |
POST /sycord/api/project_connect · Content-Type: application/json
You send
{ "name": "myapp", "stack": "nextjs" }
Syte returns JSON
{ "ok": true, "uuid": "myapp-a1b2c3", "project": { "domain": "myapp.sycord.site", "url": "https://myapp.sycord.site", "status": "created" } }
Your backend does
INSERTrow withsyte_uuid = response.uuid- Save
syte_domain,syte_urlfromresponse.project - Return your internal project id +
syte_urlto your frontend
POST /sycord/api/upload · Content-Type: multipart/form-data
You send
uuid=myapp-a1b2c3 path=app/app/page.tsx file=<binary>
Syte returns JSON
{ "ok": true, "uuid": "myapp-a1b2c3", "bytes": 2048, "message": "Uploaded…" }
Your backend does
- Load
syte_uuidfrom your DB for this project - Forward file from your user or generate code server-side
- Skip this step if the scaffold from step 1 is enough
POST /sycord/api/preview_start · Content-Type: application/json
You send
{ "uuid": "myapp-a1b2c3" }
Syte returns JSON
{ "ok": true, "preview_url": "https://previewk-myapp.sycord.site", "preview_ready": true, "preview_running": true }
Your backend does
- Show
preview_urlto user in an iframe on sycord.com or any site — embed allowed by default - If
preview_ready === false, poll step 4 every 1–2s - Stop with
POST /sycord/api/preview_stopwhen done
GET /sycord/api/preview_status?uuid=myapp-a1b2c3
Syte returns JSON
{ "ok": true, "preview_ready": true, "preview_url": "https://previewk-myapp.sycord.site", "preview_status": "running" }
Your backend does
- While
preview_ready === falseandpreview_running === true→ keep polling - When
preview_ready === true→ showpreview_url
POST /sycord/api/issue_deployment · Content-Type: application/json
You send
{ "uuid": "myapp-a1b2c3" }
Syte returns JSON
{ "ok": true, "status": "deploying", "stream_url": "/api/projects/myapp-a1b2c3/logs/stream?live=1" }
Your backend does
- Set
deploy_status = 'deploying' - Start polling step 4 every 3–5 seconds
GET /sycord/api/container_get?uuid=myapp-a1b2c3
You send
Query param uuid only (no body).
Syte returns JSON
{ "ok": true, "running": true, "url": "https://myapp.sycord.site", "status": "running" }
Your backend does
- While
running === falseandstatus === 'deploying'→ keep polling - When
running === true→ updatesyte_url = response.url,deploy_status = 'running' - Show
response.urlto user as the live site link
POST /sycord/api/domain
You send
{ "uuid": "myapp-a1b2c3", "domain": "shop.example.com" }
Syte returns JSON
{ "ok": true, "project": { "domain": "shop.example.com", "url": "https://shop.example.com" } }
Your backend does
- Update
syte_domainandsyte_urlfromresponse.project
Full backend example (Node.js)
const SYTE = process.env.SYTE_URL; // https://sycord.site
const KEY = process.env.SYTE_API_KEY;
async function createSycordProject(name, stack) {
const res = await fetch(`${SYTE}/sycord/api/project_connect`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': KEY },
body: JSON.stringify({ name, stack }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail?.message || 'connect failed');
await db.query(
`INSERT INTO projects (name, stack, syte_uuid, syte_domain, syte_url, deploy_status)
VALUES ($1,$2,$3,$4,$5,$6)`,
[name, stack, data.uuid, data.project.domain, data.project.url, data.project.status]
);
return data;
}
async function deployProject(syteUuid) {
await postJson('/sycord/api/issue_deployment', { uuid: syteUuid });
for (let i = 0; i < 60; i++) {
await sleep(5000);
const st = await getJson(`/sycord/api/container_get?uuid=${syteUuid}`);
if (st.running) return st.url;
}
throw new Error('deploy timeout');
}
At a glance
| When | HTTP | You send | You save / use |
|---|---|---|---|
| Create project | POST project_connect | {"name","stack"} | uuid → syte_uuid |
| Upload file | POST upload | multipart uuid+path+file | — |
| Dev preview | POST preview_start | {"uuid"} | preview_url |
| Poll preview | GET preview_status | ?uuid= | preview_url when ready |
| Deploy | POST issue_deployment | {"uuid"} | status=deploying |
| Poll status | GET container_get | ?uuid= | url when running=true |
| Custom domain | POST domain | {"uuid","domain"} | project.url |
| Read full guide | GET integration.json | — | Machine-readable steps |
| Read API spec | GET spec.json | — | All schemas |
uuid you saved from project_connect.project_connect creates a Syte deployment and returns a uuid your app must store.Workflow
POST /sycord/api/project_connect→ saveuuidPOST /sycord/api/upload— push files (optional if using scaffold)POST /sycord/api/preview_start— fast dev preview (next dev / vite, HMR)GET /sycord/api/preview_status?uuid=— poll until preview readyPOST /sycord/api/issue_deployment— docker build + deployGET /sycord/api/container_get?uuid=— verify runningPOST /sycord/api/domain— optional custom hostname
Stacks
| stack | Scaffolded template |
|---|---|
nextjs | Next.js 14 + Dockerfile standalone (default) |
python | FastAPI + uvicorn |
javascript | Express Node.js |
html5 | Static HTML + nginx |
Auto subdomain
On project_connect, Syte assigns https://{slug}.sycord.site (or your GUI zone). DNS wildcard must point to the server.
All endpoints except GET /sycord/api/spec.json require an API key.
| Header | X-API-Key: syte_xxxxxxxx |
|---|---|
| Alternative | Authorization: Bearer syte_xxxxxxxx |
| Create token | Syte GUI → Users → Create token, or POST /api/tokens |
curl example
curl -X POST https://sycord.site/sycord/api/project_connect \
-H "X-API-Key: syte_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "myapp", "stack": "nextjs"}'
uuid from project_connect in your database before any other call.
What to save
uuid required | Top-level response field — Syte project id (e.g. myapp-a1b2c3) |
|---|---|
persist.uuid | Same value, with save instructions |
project.uuid | Same value inside the project object |
project.domain | Assigned hostname — optional to cache |
project.url | HTTPS URL when live |
Your database example
// After project_connect — store before upload/deploy
const res = await sycordApi.projectConnect({ name: "myapp", stack: "nextjs" });
await db.projects.update({
syte_uuid: res.uuid, // REQUIRED — use for all Syte calls
syte_domain: res.project.domain,
syte_url: res.project.url,
});
Custom UUID (optional)
Pass uuid in the request body if your system already has a stable id:
{ "name": "myapp", "stack": "nextjs", "uuid": "my-custom-id-001" }
Endpoints that need uuid
POST /sycord/api/upload— form fielduuidPOST /sycord/api/preview_start— JSON{"uuid":"…"}GET /sycord/api/preview_status?uuid=POST /sycord/api/preview_stop— JSON{"uuid":"…"}POST /sycord/api/issue_deployment— JSON{"uuid":"…"}GET /sycord/api/container_get?uuid=POST /sycord/api/domain— JSON{"uuid":"…","domain":"…"}
Creates workspace, scaffolds stack, assigns https://{slug}.sycord.site.
Request body
| name required | Project name — subdomain slug derived from this |
|---|---|
| stack | nextjs · python · javascript · html5 (default nextjs) |
| uuid | Optional custom Syte project id |
| env_vars | Optional {"KEY":"value"} object |
{
"name": "testproject",
"stack": "nextjs",
"env_vars": {}
}
Response — save uuid
{
"ok": true,
"uuid": "testproject-a1b2c3",
"message": "Empty project created. Scaffolded: app/package.json…",
"persist": {
"save_uuid": true,
"uuid": "testproject-a1b2c3",
"instruction": "Save uuid in your application database…",
"endpoints_using_uuid": ["POST /sycord/api/upload", "…"]
},
"project": {
"uuid": "testproject-a1b2c3",
"name": "testproject",
"domain": "testproject.sycord.site",
"url": "https://testproject.sycord.site",
"stack": "nextjs",
"status": "created",
"port": 3010,
"workspace_path": "/var/syte/workspaces/testproject-a1b2c3",
"app_path": "/var/syte/workspaces/testproject-a1b2c3/app",
"created_at": "2026-07-04T18:00:00Z"
},
"subdomain_pattern": "{slug}.sycord.site",
"next_steps": {
"save_uuid": "testproject-a1b2c3",
"upload": "POST /sycord/api/upload",
"preview": "POST /sycord/api/preview_start",
"deploy": "POST /sycord/api/issue_deployment",
"container": "GET /sycord/api/container_get?uuid=testproject-a1b2c3"
}
}
| uuid required | Query param — from project_connect |
|---|
{
"ok": true,
"uuid": "testproject-a1b2c3",
"container_name": "syte-testproject-a1b2c3",
"exists": true,
"running": true,
"state": "running",
"image": "syte-testproject-a1b2c3",
"url": "https://testproject.sycord.site",
"domain": "testproject.sycord.site",
"host_port": 3010,
"status": "running"
}
multipart/form-data
| uuid required | Syte project uuid |
|---|---|
| path required | Relative path e.g. app/app/page.tsx |
| file required | File binary |
curl -X POST https://sycord.site/sycord/api/upload \ -H "X-API-Key: syte_…" \ -F "uuid=testproject-a1b2c3" \ -F "path=app/app/page.tsx" \ -F "file=@page.tsx"
Starts next dev or vite in seconds — no Docker build. Requires a dev script in package.json.
{ "uuid": "testproject-a1b2c3" }
{
"ok": true,
"uuid": "testproject-a1b2c3",
"message": "Preview on https://previewk-testproject.sycord.site — ready (HMR live)",
"preview_url": "https://previewk-testproject.sycord.site",
"preview_domain": "previewk-testproject.sycord.site",
"preview_ready": true,
"preview_running": true,
"preview_port": 4001,
"preview_status": "running",
"preview_stream_url": "/api/projects/testproject-a1b2c3/preview/logs/stream?live=1"
}
Poll every 1–2s after preview_start until preview_ready=true.
{
"ok": true,
"uuid": "testproject-a1b2c3",
"preview_url": "https://previewk-testproject.sycord.site",
"preview_ready": true,
"preview_running": true,
"preview_status": "running"
}
{ "uuid": "testproject-a1b2c3" }
{ "ok": true, "uuid": "testproject-a1b2c3", "message": "Preview stopped", "preview_running": false, "preview_status": "stopped" }
{ "uuid": "testproject-a1b2c3", "domain": "custom.sycord.site" }
Caddy issues TLS once DNS points to the server.
{ "uuid": "testproject-a1b2c3" }
Runs in background. Stream logs: GET /api/projects/{uuid}/logs/stream?live=1
{
"ok": true,
"uuid": "testproject-a1b2c3",
"message": "Deploy issued…",
"stream_url": "/api/projects/testproject-a1b2c3/logs/stream?live=1",
"status": "deploying"
}
Error format
{ "detail": { "error": "not_found", "message": "Project not found" } }
| 401 missing_api_key | No X-API-Key header |
|---|---|
| 401 invalid_api_key | Token revoked or wrong |
| 400 invalid_stack | Unknown stack value |
| 400 connect_failed | Subdomain taken or validation failed |
| 400 upload_failed | Bad path or missing project |
| 400 preview_failed | No dev script or preview process exited |
| 404 not_found | uuid does not exist |