`POST /api/apps/{id}/runs` -- multipart submit. Authenticated by EITHER a logged-in session (the operator) OR a per-deployment App API token (`Authorization: Bearer scwa_…`, supacloud#410 — a server-side caller now that a hosted App on the separate content domain cannot ride the session). The token must own this `app_id`; the run is attributed to the App's deployer.
const url = 'https://example.com/api/apps/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/runs';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'multipart/form-data'}};
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/apps/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/runs \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: multipart/form-data'Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”App id
Request Bodyrequired
Section titled “Request Bodyrequired”One form field per manifest input; file-typed fields are streamed.
Responses
Section titled “Responses”Run opened; uploads persisted.
Response body of POST /api/apps/{id}/runs — the run opened for the submit
plus the metadata of every persisted file upload. Spec mirror of the JSON the
submit_inner handler serializes ({ "run_id", "uploads": [...] }); the
runtime builds the same shape via serde_json::json!, so this keeps the
contract faithful without changing the runtime path.
object
The id of the app run opened for this submit (poll the runs API with it).
One entry per persisted file-typed field, in submit order.
Wire-shape upload view returned by the upload HTTP endpoint – mirrors
the row 1:1 with sha256 rendered as hex.
object
Examplegenerated
{ "run_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "uploads": [ { "app_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "expires_at": "2026-04-15T12:00:00Z", "field_name": "example", "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "mime": "example", "original_filename": "example", "run_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "sha256_hex": "example", "size_bytes": 1, "storage_backend": "example", "uploaded_at": "2026-04-15T12:00:00Z", "workspace_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" } ]}Unknown/missing/duplicate field or malformed multipart body.
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"}Missing/invalid App token or session.
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"}App token does not authorize this app.
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"}App not visible in this workspace.
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"}A file field exceeds its size cap.
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"}MIME-type mismatch on a file field.
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"}Storage I/O failure.
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"}