`POST /api/apps/{id}/backend/{function_name}` -- invoke a declared backend function synchronously (ADR 0053). The author-fixed SQL runs under the db_query/db_execute guard with RLS `supacloud.app_id` injected; only the function name + positional args cross the wire, so a caller cannot inject SQL. Authenticated by EITHER a per-deployment App token OR a session.
const url = 'https://example.com/api/apps/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/backend/example';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"args":[{}]}'};
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/backend/example \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "args": [ {} ] }'Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”App id
Declared backend function name
Request Bodyrequired
Section titled “Request Bodyrequired”POST /api/apps/{id}/backend/{function_name} request body – the positional
arguments bound to the function’s $1..$n placeholders. The function name is
a path segment, not a body field; the SQL is author-fixed in the manifest.
object
Positional arguments, bound in order. Empty for a no-arg function.
object
Examplegenerated
{ "args": [ {} ]}Responses
Section titled “Responses”Function result (rows / affected count).
POST /api/apps/{id}/backend/{function_name} response. operation tells the
caller how to read the payload: a query returns rows with
row_count = rows.len(); an execute returns the affected-row count in
row_count and any RETURNING rows in rows.
object
The invoked function name.
query or execute.
script only – the arbitrary JSON value the script returned (a query/
execute returns null here and carries its payload in rows). Omitted
from the wire when absent, so the query/execute shape is unchanged. Typed
as an UNCONSTRAINED value (a script may return an array/scalar/object) so the
generated zod is z.unknown(), not a z.object({}) that would reject a
non-object return or strip an object’s keys (the §10 result-shape defect).
Affected-row count (execute) or returned-row count (query).
Query rows, or DML RETURNING rows (empty when the function does not
return). Each row is a free-form object — typed as a key-preserving map so
the generated zod becomes z.array(z.object({}).catchall(...)) instead of a
strict z.array(z.object({})) that zod v4 strips every row column from on
parse (the §10 result-table contract defect). Mirrors the proven
audit_events.payload_redacted shape.
object
Examplegenerated
{ "function": "example", "operation": "example", "result": "example", "row_count": 1, "rows": [ { "additionalProperty": "example" } ]}Bad args, mis-declared SQL, or no app_db bound.
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 or function not found.
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"}