Cloud
Connect installations to Cloud and use its HTTP control endpoints.
Hosted-cloud only, switched off in Synq
These routes are for connecting to a hosted cloud. The hosted cloud is switched off in Synq, so the dashboard never offers them and a Synq install has nothing to connect to or pay for. They are documented here because the routes still exist in the code.
Cloud endpoints connect a self-hosted installation to Synq Cloud and support browser handoffs,
managed routing, and transfers. The self-hosted connection routes and Cloud control-plane routes share
/api/cloud; the process mode determines which are available.
There is no named native cloud group. To manage Cloud projects through the SDK, use the
remote client with the canonical Cloud API and Cloud organization ID.
A local owner's Cloud link does not provide verified mapping for a fixed SDK scope; see
Cloud compatibility.
Self-hosted endpoints
Present only on a self-hosted instance (not in CLOUD_MODE). The cloud connection is org-owned: it
belongs to the organization owner, so every member sees the same verdict. Disconnect and connect-finalize
require the owner role, not merely a cloud:admin grant.
Check connection status
GET /api/cloud/statuscurl "$SYNQ_URL/api/cloud/status" \
-H "Authorization: Bearer $SYNQ_TOKEN"Connecting itself is a browser flow started from the dashboard (Settings → Cloud): a popup runs the
PKCE handshake against Synq Cloud, then posts the result to connect-finalize below. There is no
dedicated synq cloud connect CLI verb.
Finalize a connection
The dashboard popup reads its PKCE verifier from localStorage and posts the returned code here to store
the org's cloud bearer.
POST /api/cloud/connect-finalizeProp
Type
Owner only
connect-finalize and disconnect take over the org's cloud bearer, so they are gated by requireRole("owner")
on top of the cloud:admin tag. A non-owner with a cloud:admin grant still gets 403.
Cloud control-plane endpoints
Present only in cloud mode (CLOUD_MODE) — i.e. these run on Synq Cloud, not on your self-hosted box.
All tenant-scoped operations forward to Synquic through the caller's namespace token; Synquic enforces the
namespace boundary natively, so there is no SaaS-side ownership ledger. The public routes carry no session
— they are reached by browser redirects and authenticated by a signed, single-use token in the URL.
These are mostly internal
Your self-hosted instance calls the control-plane endpoints for you via its cloud client — you don't wire
them up by hand, and there is no synq CLI verb that maps to them one-to-one. The request shapes below
are documented so integrators can reason about the traffic.
Handoff & code exchange
The connect and desktop-login flows never hand the local instance a browser cookie. Instead a short-lived,
PKCE-bound handoff code is minted (connect-authorize), then swapped for a dedicated session
(exchange-code).
POST /api/cloud/connect-authorizeProp
Type
POST /api/cloud/exchange-codeProp
Type
The public GET handoff routes take query params, not a body — desktop-handoff and connect-handoff
expect redirect, state, and code_challenge; github/install-callback expects installation_id,
setup_action, and state; github/oauth-bridge expects token.
Deploy proxies (preflight, edge proxy, analytics)
POST /api/cloud/preflightProp
Type
POST /api/cloud/edge-proxyProp
Type
POST /api/cloud/analyticsProp
Type
Pages
pages creates a managed static page; the enable/disable/delete actions each take just a slug.
POST /api/cloud/pagesProp
Type
POST /api/cloud/pages/disable (also /enable, /delete)Prop
Type
Invitation relay
Used when a self-hosted operator sets invitationMailSource = "cloud" — the SaaS sends the email from its
own infrastructure. Capped at 20/hour per org inside the service.
POST /api/cloud/send-invitationProp
Type
Subgraph transfer & teardown
The ingest/export pair moves an org's or a single project's rows between a self-hosted instance and the SaaS (team-mode migration and project transfer). Instance-scope dumps are refused.
POST /api/cloud/ingest-subgraphProp
Type
POST /api/cloud/export-subgraphProp
Type
POST /api/cloud/teardown-projectProp
Type
GitHub App proxy
Cloud holds the GitHub App private key; self-hosted instances never do. Local hands off (userId, request)
and gets resolved data back — it never signs a JWT or sees the App credentials.
POST /api/cloud/github/installation-tokenProp
Type
installationId is never accepted from the body
installation-token resolves the installation from the caller's org, not from a client-supplied id — passing
one has no effect. This prevents a caller from minting a token for an installation it doesn't own.
Errors
403 — owner required (self-hosted connect/disconnect)
connect-finalize and disconnect require the org owner role. A member with only a cloud:admin grant is
rejected.
409 — slug already taken (edge proxy / pages)
The requested slug is owned by another tenant on the shared cloud zone. Synquic returns 409 and Synq
forwards it. Choose a different slug.
412 — dump format mismatch (ingest)
ingest-subgraph rejects dumps whose formatVersion doesn't match the SaaS (code: INGEST_FORMAT_MISMATCH).
Update your local instance and re-dump.
413 — dump too large (ingest)
ingest-subgraph caps the request body at 50MB (code: PAYLOAD_TOO_LARGE) before auth even runs.
401 — invalid or expired code / session
Handoff codes are single-use and time-bound; exchange-code returns 401 once consumed or expired. The
control-plane cloud:* endpoints return 401 when the cloud session bearer is missing or revoked — reconnect
from the dashboard.
Operations
| Operation | REST API |
|---|---|
| The connected cloud account (name, email, avatar). | GET /api/cloud/accountcloud:read |
| Proxy Synquic analytics for a hostname in the caller's namespace. | POST /api/cloud/analyticscloud:write |
| Consent page mints a one-time handoff code (cookie session checked inline). | POST /api/cloud/connect-authorizeHandler authentication |
| Complete the connect popup: exchange the PKCE code and store the cloud bearer. | POST /api/cloud/connect-finalizecloud:admin |
| Validate connect params, then 302 to the dashboard consent page. | GET /api/cloud/connect-handoffHandler authentication |
| Poll the local Cloud connection handoff. | GET /api/cloud/connect-pollHandler authentication |
| OAuth → one-time code → redirect to the desktop app callback. | GET /api/cloud/desktop-handoffHandler authentication |
| Revoke the current cloud session server-side (idempotent). | POST /api/cloud/disconnectcloud:admin |
| Sync the managed edge proxy (bind a slug to a target). | POST /api/cloud/edge-proxycloud:write |
| Remove an authorized Cloud edge-proxy route. | POST /api/cloud/edge-proxy/deletecloud:write |
| Begin verification for an edge-proxy domain. | POST /api/cloud/edge-proxy/verifycloud:write |
| Check edge-proxy domain verification. | POST /api/cloud/edge-proxy/verify-checkcloud:write |
| Exchange a one-time code for user + session (rate-limited). | POST /api/cloud/exchange-codeHandler authentication |
| Export an org- or project-scope subgraph dump (rate-limited). | POST /api/cloud/export-subgraphcloud:admin |
| GitHub install callback — atomically claim a durable user/workspace nonce after matching user-token and Synq App-JWT verification. | GET /api/cloud/github/install-callbackHandler authentication |
| Build the App install URL with a one-time state token. | POST /api/cloud/github/install-urlcloud:write |
| Mint a short-lived (~60min) installation access token. | POST /api/cloud/github/installation-tokencloud:write |
| List the org owner's GitHub App installations. | GET /api/cloud/github/installationscloud:read |
| Consume the bridge token and redirect the browser into GitHub OAuth. | GET /api/cloud/github/oauth-bridgeHandler authentication |
| Start GitHub OAuth: return a one-time bridge URL. | POST /api/cloud/github/oauth-handoffcloud:write |
| Friendly "GitHub connected" close-window page. | GET /api/cloud/github/oauth-successHandler authentication |
| Cloud-resolved GitHub OAuth identity (login, avatar) for the caller. | GET /api/cloud/github/user-statuscloud:read |
| Ingest an org- or project-scope subgraph dump (rate-limited, 50MB cap). | POST /api/cloud/ingest-subgraphcloud:admin |
Create a Cloud Page (proxy Synquic pages.create). | POST /api/cloud/pagescloud:write |
| Delete a Cloud Page by slug. | POST /api/cloud/pages/deletecloud:write |
| Disable a Cloud Page by slug. | POST /api/cloud/pages/disablecloud:write |
| Enable a Cloud Page by slug. | POST /api/cloud/pages/enablecloud:write |
| Cloud deployment preflight — slug / custom-domain availability. | POST /api/cloud/preflightcloud:write |
| Run an allowed Pages or routing action after verifying customer namespace ownership. | POST /api/cloud/resource-proxycloud:write |
| Read routes or certificates belonging to the caller's Cloud namespace. | GET /api/cloud/route-registrycloud:read |
| Relay an invitation email via the SaaS mail infrastructure. | POST /api/cloud/send-invitationcloud:write |
| Synq Cloud connection status for this instance. | GET /api/cloud/statuscloud:read |
| Delete a project's rows on the SaaS (bring-home / reconcile). | POST /api/cloud/teardown-projectcloud:admin |
| Mint a namespace-scoped Synquic token for the caller's org. | POST /api/cloud/tokencloud:write |
| List the org's Synq Cloud (Synquic) workspaces, with drift (orphaned cloud/local). | GET /api/cloud/workspacescloud:read |