Grants & Client Access
The Apps contract in practice: resource grants that allow what an app may read or act on, per-key client grants, and pickable resource and client endpoints.
Apps access is two orthogonal grant families. Resource grants (/v1/apps/:id/grants) are the app's capability policy: what the *app itself* may read or act on. Client grants (/v1/apps/:id/clients) govern what one *API key* may do to the app. Effective power is always the intersection — token grant ∩ app capability policy — layered after tenant isolation and Sources-IAM. Resource grants are the platform's first allow-model primitive: absence of a row means the app cannot read the resource, whatever the calling token holds.
A resource grant names a resource_type (data_product, reference_table, source_connection, app, or action), a resource_id (a UUID for platform resources; the action name for type action), and an access level (read or act). Every input binding of a published version must be covered by a read grant, and every entry in acts by an act grant of type action — the input assembler checks grants live on every run, so revocation takes effect immediately, with no caching.
A client grant pairs an api_key_id with a grant_level: read (see the app, its runs and ledger), operate (run, dry-run, configure, resolve non-approval reviews, enable/disable), or decide (External mode: claim decision tasks — implying nothing else beyond the journaled disclosure of a claimed task's input package). Reading either list requires only the read tier, but every mutation is owner-only and human-only: machine callers get 403 Grant and client management requires a human workspace owner, whatever scopes they hold.
/v1/apps/:id/grantsBody
/v1/apps/:id/clientsBody
curl — inspect access from either side
# What may this app read or do?
curl -s https://api.talonic.com/v1/apps/$APP_ID/grants \
-H "Authorization: Bearer tlnc_your_api_key"
# → { "grants": [ { "id": "…", "resource_type": "data_product",
# "resource_id": "1f6f2f57-…", "access": "read", "created_at": "…" } ] }
# Which keys may touch this app, at what level?
curl -s https://api.talonic.com/v1/apps/$APP_ID/clients \
-H "Authorization: Bearer tlnc_your_api_key"
# → { "clients": [ { "id": "…", "api_key_id": "22e9f8d8-…",
# "grant_level": "operate", "created_at": "…" } ] }Response (403) — grant mutation from a machine caller
{
"statusCode": 403,
"code": "INSUFFICIENT_PERMISSIONS",
"error": "forbidden",
"message": "Grant and client management requires a human workspace owner.",
"retryable": false,
"request_id": "req_8a4e83d8fcfa4dc7",
"path": "/v1/apps/80afca3e-c66c-4f07-abbf-78913ee80dff/grants"
}Pickable resources and clients
Two helper endpoints keep grant forms free of pasted UUIDs. GET /v1/apps/pickable-resources?type=<resource_type> (read tier) lists grantable resources with human labels — { items: [{ id, label, sublabel }] }, e.g. a data product's name with its description, a reference table with its row count, an app with its slug; ?exclude=<uuid> omits one id (typically the app being configured, since an app never binds itself). GET /v1/apps/pickable-clients (owner tier, human only) lists the workspace's API keys as pickables for the client-grant form — it lives on the Apps surface rather than /v1/account/keys so the web session reaches it through the dual-auth head.
Revocation is symmetric and instant: DELETE /v1/apps/:id/grants/:grantId removes a capability, DELETE /v1/apps/:id/clients/:grantId removes a key's level — both owner-only, both answering { "revoked": true }, both effective on the next access check because grants are read live. Deleting an app or a key cascades its grant rows away.