---
title: "Permissions"
description: "Groups, the union rule, and what \"cannot read\" means for an API, an OpenAPI document or an agent. The same rules apply on every surface."
canonical: "https://gtable.app/docs/start/permissions"
updated: "2026-10-05"
---

# Permissions

Every read and every write in an app, from the grid, the API, MCP or the built-in assistant,
goes through the same permission check. There is no route around it, and no key that skips it.

## The model

A builder gives each app **groups**, and each group its own permissions: on which tables it may
do which operations, optionally narrowed to some rows and some fields. A person can be in
several groups. Their access is the **union** of their groups, taken per field and per
operation.

There is no order, no deny and no override. Adding a group can only ever add access, and what
no group allows, nobody has. No group is special to the platform: there is no admin group in an
app. Administration happens in the Studio, on the other plane.

## Four questions, answered separately

| Question      | Example                                |
| ------------- | -------------------------------------- |
| Which tables  | Support cannot see invoices at all     |
| Which rows    | A rep sees the deals they own          |
| Which fields  | Nobody outside Finance sees the margin |
| Which actions | Read yes, delete no                    |

## What "cannot read" means

A field you cannot read is **absent**, not blanked. It is not in the record, not in the schema
you are served, not in your OpenAPI document, not in your agent's tool list, and not named in
any error, because naming it would be the very leak the rule exists to prevent. A row you cannot
see is answered as if it did not exist.

That is also why an app's API reference on this site names no table and no field. Each person's
document is generated from their own view of the app. See
[Why an app has an API of its own](/docs/start/studio-and-runtime#why-an-app-has-an-api-of-its-own).

## Filters cannot be used to guess

A filter can only narrow what you would see anyway. One case needs a rule: a field you may read
on only **some** of your rows ("Finance sees the margin, on won deals"). Filtering or sorting by
it would let you count rows outside your scope, so it is refused with `403`. `GET /v1/app`
lists, per table, the fields you can see but not filter or sort by (`constrained`), so a client
can grey them out before asking.

## Credentials narrow, never widen

A key or an agent connection carries scopes (`records:read`, `records:write`, …). Scopes narrow
what a credential may do; they never add to what its person may do. A key with `records:write`,
made by someone who may only read, still cannot write. See
[Authentication](/docs/start/authentication).

## Before changing a rule

Builders can ask what someone would see, without granting anything. `rules.simulate` answers
with the rows and fields a set of groups would get, broken down per group so you can see which
group grants what:

```http
POST https://studio.gtable.app/v1/apps/{appId}/rules/simulate
```

See [Ask what someone would see](/docs/builder-api/rules/simulate).
