Orkestia
Blog
App Data

Records & Data API

Create, read, query, update, and delete App Data rows through typed workflows or the App Data MCP — structured filters, never SQL from the browser

Once a structure is applied, rows move through record workflows or the App Data MCP / Data API. Both enforce the same principal, workspace, and capability rules. Neither accepts SQL.

Record workflows

The data.appdata.record.* family is the engine-native surface (org-member token or an exposed virtual wrapping these steps):

IntentTypical type (discover the live name)
Insertdata.appdata.record.write
Read onedata.appdata.record.read
Querydata.appdata.record.query
Updatedata.appdata.record.update
Delete (soft)data.appdata.record.delete
Batchdata.appdata.transaction.apply
Documentsdata.appdata.document.request-upload / .confirm / .query / .download-url / .delete
document.* needs an end-user principal and a storage.* connection. Org-member files on the hosted site are apphost.file.*, not this family.

database_slug may be omitted when the authenticated app has exactly one serving database. If the app has zero or several, name the slug — otherwise the run fails closed. Prefer passing it whenever you know it.

Writes take an optional durable idempotency_key. The same key plus the same payload replays; the same key plus a different payload is a conflict.

From the Node SDK:

import { LtIntegWorkflowsClient } from "@ltinteg/workflows-sdk"

const client = new LtIntegWorkflowsClient({
  baseUrl: "https://workflow-api.orkestia.dev",
  token: process.env.ORKESTIA_TOKEN,
})

const run = await client.start("data.appdata.record.query", {
  /* table + filters from the live schema */
})
const rows = await run.wait()
End-user browsers must not start data.appdata.record.write (or its siblings) by catalog name. Compose those steps into a virtual and expose it. The browser starts virtual.<uuid>@<version> with the @orkestia/auth JWT.

Deletes are soft. A deleted row is hidden from live reads; it is not gone forever.

App Data MCP (Data API)

For a signed-in app user (or an agent acting as one), the App Data MCP is a seven-tool Data API. You do not list the whole catalog into the conversation. You do not send SQL.

discover({ query: "properties" })     → small ranked list of views / functions
describe({ resource: "property" })    → columns, filters, kind
read({ resource, where, limit })      → rows
create({ resource, row })
update({ resource, row, where })      → where is mandatory
delete({ resource, where })           → where is mandatory; soft delete
call({ function, args })              → RPC, not SQL

where is a structured object, for example { "hectares": { "gte": 50 } }. Never send SELECT, semicolons, or comments.

// Shape only — column names come from describe(), not from this page.
await read({
  resource: "property",
  where: { active: { eq: true } },
  order: [{ field: "name", dir: "asc" }],
  limit: 20,
})

Workspace

If the table is organization-owned, pass the app workspace (the app-organization UUID) when the user is inside one. Do not invent it. List the user's workspaces through the identity workflows first — see Ownership.

What the Data API hides

  • Physical tables (_t_*) are not part of the API.
  • Live views already hide soft-deleted rows.
  • Column names come from describe, not from memory or this page.

Isolation (what the platform guarantees)

  1. Principal binding — end_user_uuid and identity_app_uuid come only from the verified token metadata. A caller-supplied value that disagrees is rejected.
  2. Active workspace — for ownership.kind = organization, the workspace id is server-resolved. Absent → fail closed.
  3. Capability gate — {namespace}:{verb} (and optional resource ref) runs before the handler. A denied read is a failed workflow, not an empty list.
  4. Owner filter — owner-scoped tables always filter to the bound principal.

Every invocation is audited (allowed or denied).

Declare

Tables and ownership first.

Expose

Safe path for a browser JWT.

Workflow SDKs

Start record workflows from Node or Python.

PostgREST HTTP

Standard PostgREST client, end-user JWT, public JWKS.

Query console

Operator SELECT — not this Data API.