Handler: clone the repo, scan it, apply the plan, return the report.
const url = 'https://example.com/api/workflows/import/windmill/repo';const options = { method: 'POST', headers: { cookie: 'supacloud_session=<supacloud_session>', 'Content-Type': 'application/json' }, body: '{"credential_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","db_resource":"example","git_ref":"example","git_url":"example","project_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/api/workflows/import/windmill/repo \ --header 'Content-Type: application/json' \ --cookie supacloud_session=<supacloud_session> \ --data '{ "credential_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "db_resource": "example", "git_ref": "example", "git_url": "example", "project_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" }'Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”POST /api/workflows/import/windmill/repo body.
object
Optional git_credentials row id (an access_token credential) whose
token is the HTTPS PAT used for the clone. DB-backed + workspace-pickable,
so it works on the env-only secret backend. Omit for a public repo.
Optional postgresql resources row (by NAME or UUID) the repo’s shared
db namespace (db/migrations/) binds to (ADR 0057). This is the one
deployment-specific fact a generic windmill repo can’t carry — picked in
the wizard — so it binds the INFERRED namespace and overrides any
repo-authored db/namespace.yaml resource. Omit when the repo has no
db/migrations/, or carries a self-contained db/namespace.yaml.
Branch to clone. Defaults to main when omitted.
HTTPS clone URL of the windmill-workflows repo.
The project the imported workflows are created in.
Examplegenerated
{ "credential_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "db_resource": "example", "git_ref": "example", "git_url": "example", "project_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}Responses
Section titled “Responses”Per-flow import report
The full result of applying an ImportPlan to a project.
object
Flows the scanner could not convert (surfaced through from the plan).
A flow the scanner could not convert at all (surfaced straight through from
ImportPlan::blocked so the operator sees the whole picture in one report).
object
The shared db namespace (db/namespace.yaml + db/migrations/) result,
when the repo ships one on a BYO DEDICATED postgresql resource (ADR 0057).
None when there is no namespace OR when the import bound the built-in App DB
(that path is per-App — see per_app_db).
object
Migration names actually applied to the schema this run.
Per-step failures (resource/namespace resolution, a bad migration, apply).
One migration (or resolution step) that failed during a namespace import.
object
The namespace handle from the manifest.
The namespace row id (nil if resolution failed before creation).
Migration names recorded into db_namespace_migrations.
The resolved schema name (the manifest’s schema, else the handle).
Raw-apps detected but not persisted (same-slug clash / persist error).
A raw-app detected but NOT persisted (e.g. a same-slug App already exists, or the persist failed). Carries the operator-facing note + a reason.
object
HTTP triggers detected — DETECT-only, the operator re-wires them.
A generic DETECT-and-record item (HTTP trigger / schedule) the operator must re-wire by hand — SupaCloud has no 1:1 import for these.
object
Schedules detected — DETECT-only, the operator re-creates them.
A generic DETECT-and-record item (HTTP trigger / schedule) the operator must re-wire by hand — SupaCloud has no 1:1 import for these.
object
Flows that converted but failed to persist (one bad flow, rest continue).
A flow that converted but failed to persist (a per-flow error — the rest of
the plan still imports). Distinct from the scanner’s blocked (failed to
convert).
object
Flows that persisted as new workflows.
One flow that persisted into the project.
object
The scanner’s fidelity badge, as a stable lowercase label
(clean | needs_rebind | blocked) — keeps the report serializable
without making the internal Fidelity enum Serialize.
Raw-apps that persisted as React Apps.
A raw-app that persisted as a SupaCloud App (framework = react).
object
Number of source files written into the App’s app_files tree.
First-class scripts imported as SupaCloud Script rows (never dropped).
A first-class Windmill script imported as a SupaCloud Script row. Carries the
LOUD needs_mapping review notes (windmill APIs the source uses that must be
adapted before it runs on the substrate) — the importer surfaces the script,
it never silently drops it.
object
The display name the Script row was created with.
Windmill-specific APIs to adapt before the script runs (empty ⇒ none flagged).
The created Script row id.
The windmill script path (f/shared/finance_overview).
Custom resource types an operator must wire (not auto-created).
A custom resource type the operator must wire to a SupaCloud resource/credential before its flows can run. We never auto-create credentials, so each resource type is surfaced here, not applied.
object
The per-App installs of the repo’s shared schema into each imported App’s own
app_<id> schema (#626 §14 — the built-in App DB path). Empty unless the
import bound the built-in App DB and the repo ships db/migrations/.
The result of installing a repo’s shared schema into ONE imported App’s own
per-App app_<id> schema (built-in App DB path).
object
The App the schema was installed into.
The App’s display name (echoed for the report).
Migration names newly applied into this App’s schema this run.
Per-migration failures (a failure blocks the rest — append-only migrations are ordered, so a mid-set failure would mis-attribute if we continued).
One migration (or resolution step) that failed during a namespace import.
object
The per-App schema the repo migrations were applied to (app_<id>); empty
only when the very first migration failed before any apply resolved it.
Flows skipped because a same-named workflow already exists in the project.
A flow skipped because a workflow with the same name already exists in the project (idempotent re-import — cheap skip-by-name, no duplicate row).
object
First-class scripts detected but not imported (idempotent same-slug skip / unsupported-language create error) — surfaced, never silently dropped.
A first-class script detected but NOT imported — an idempotent same-slug skip on re-import, or a create error (e.g. a language not yet supported as a standalone Script). Surfaced so the operator sees it; NEVER silently dropped.
object
Examplegenerated
{ "blocked": [ { "reason": "example", "source_path": "example" } ], "db_namespace": { "applied": [ "example" ], "failed": [ { "name": "example", "reason": "example" } ], "handle": "example", "namespace_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "recorded": [ "example" ], "schema": "example" }, "detected_apps": [ { "name": "example", "note": "example", "reason": "example", "source_path": "example" } ], "detected_http_triggers": [ { "name": "example", "note": "example", "source_path": "example" } ], "detected_schedules": [ { "name": "example", "note": "example", "source_path": "example" } ], "failed": [ { "reason": "example", "source_path": "example" } ], "imported": [ { "fidelity": "example", "name": "example", "source_path": "example", "warnings": [ "example" ], "workflow_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" } ], "imported_apps": [ { "app_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "file_count": 1, "name": "example", "source_path": "example" } ], "imported_scripts": [ { "name": "example", "needs_mapping": [ "example" ], "script_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "source_path": "example" } ], "needs_mapping": [ { "name": "example", "source_path": "example" } ], "per_app_db": [ { "app_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "app_name": "example", "applied": [ "example" ], "failed": [ { "name": "example", "reason": "example" } ], "schema": "example" } ], "skipped": [ { "name": "example", "reason": "example", "source_path": "example" } ], "skipped_scripts": [ { "name": "example", "reason": "example", "source_path": "example" } ]}Structured client error
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Authentication required
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Permission denied
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Structured server error
The canonical JSON body of every error response — the single source of truth
the frontend binds to. Every AppError serializes as this exact shape, and
the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is
what the frontend error schema is generated from, so there is no hand-written
error schema on either end.
object
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}