Data, access and sources
An app's data lives in Records: typed tables your organisation owns. Roles decide who sees which rows and fields. Data can also come from a system you already run, live through each person's own connection, or synced into Records on a schedule.
Record types and fields
- A record type is a table: requests, assets, risks. Each has a primary field (the record's name) and fields of these types: text, number (with formats such as currency), date and time, yes or no, choice (one option, or several, from a list) and lookup (a link to a record of another type).
- Every record also has an owner, a created time and an updated time, and a version number that writes check.
- A field can be marked sensitive. Sensitive fields are masked for every role without an explicit grant, including agents, and never reach an API key unless the key is granted the field by name.
- Deleted records are archived, not destroyed, and can be restored.
Roles, row scope and field access
| Setting | What it controls |
|---|---|
| Capabilities | Per role: read, create, update, delete, export, manage the app (design and publish) and manage access (roles and people). An app's Maker role has all of them; the templates also ship a Viewer role with read. |
| Row scope | All rows of the type, or owned: only records the person owns. Workspace scope exists in the contract but is not switched on: records are organisation-wide. |
| Field access | Per role and field: hidden (left out of pages, queries and the API; a filter on it reads as an unknown field), masked (shown as masked, never the value), read only, or write (an explicit grant, which is how a role is given a sensitive field). |
| Audience | Interfaces, pages and actions can be limited to roles. A person sees only what their roles allow, and the server checks the audience on every request. |
Agents reading an app's data get the same row scope and field access as the person they work for, with sensitive fields at most masked even for an administrator. API keys see a service view: never a sensitive field, and never a field the app's default Viewer role hides or masks, unless the key is granted it by name. API and webhooks.
Data sources
By default a page reads Records. A maker can add up to 10 data sources to an app and bind pages, components and actions to them. A source is live (read, and optionally written, where the data is) or synced (copied into a Records type on a schedule).
| Source | What you set, and how it reads |
|---|---|
| Dataverse (Dynamics 365) | The environment URL and a table (logical name). Reads up to 5,000 rows. Can write when the source allows writes. |
| Salesforce | An object (API name). Pages with an offset of up to 2,000, then by id for larger reads. Can write when allowed. |
| SharePoint Lists | A site and a list. Pages with SharePoint's own paging; contains, empty checks and lookup columns are filtered in memory. Writes send the item's etag. Can write when allowed. |
| Excel tables | A workbook in SharePoint or OneDrive and a table. Excel has no server-side filter, so rows are read in pages up to 5,000 and filtered in memory. Writes compare a digest of the row and re-check the values in the workbook. Can write when allowed. |
| SQL (PostgreSQL or SQL Server) | An organisation SQL connection and an allowed table or view. Read only, 500 rows a page. SQL connections. |
Live sources use the viewer's own connection
- A live source calls the provider with the connection of the person viewing the page. If they have no connection to that provider, the component says Connect <provider> to see this and offers the connect flow.
- Sharing an app never grants access to external data. People see what their own account in that system lets them see.
- Writes to a live source are off unless the maker turns them on for that source. SQL is always read only. A write goes through the same write rules, which are evaluated against the external row after the change. A held write is replayed as the requester when approved, only while the source still points where it did and the row still has the etag it had.
- Every applied write to a live source is audited and sealed into the evidence trail. External rows have no Records event, and there is no related or timeline component for them.
- Joins across live sources are not supported. Use sync.
Sync into Records
- A synced source copies one entity (a table, object, list or Excel table) into a Records type, one way, every 15 minutes to 7 days, with a field map from Records fields to source fields.
- It runs as the app owner's connection. The owner gives consent when setting it up; the consent is bound to the source's settings and to the current owner, so a changed source or a new owner needs consent again.
- Rows are matched by their external id (Excel rows by their first column). Rows missing at the source are archived, except when a read stopped at 5,000 rows, which archives nothing.
- The copy is then governed like any Records type: roles, field access, write rules, triggers. It cannot write back.
- Run history is shown in the designer's Data sources section. A sweeper checks for due syncs every five minutes.
SQL connections
Organisation owners and admins add SQL connections under Organisation settings > Governance > Apps > SQL connections, and pick the tables and views apps may read.
- Engines: PostgreSQL and SQL Server. The password is encrypted for your organisation and never returned. TLS is always on; verify full checks the certificate, require skips the check for a private CA.
- The host must resolve to a public address. Private, loopback, link-local and other reserved addresses are refused, including a redirect to one (as an Azure SQL gateway may issue), unless the operator lists the host in an allow-list.
- Queries are generated from the closed grammar as parameterised SQL against an allowed table. Nobody, person or agent, can send SQL text. Each query runs in a read-only transaction that is always rolled back; for SQL Server, connect with a read-only database user.
- Limits: a 10 second connect timeout, a 15 second statement timeout, 500 rows a page.
Import and export
- Import a spreadsheet into a Records type from the app's Data view. If a write rule matches, the whole import is held as one request.
- Export a type's records from the Data view. An export job writes up to 50,000 rows; a larger result is marked as truncated.
- A report component exports its rows as CSV (UTF-8 with a byte order mark). Text that a spreadsheet would run as a formula is prefixed with an apostrophe.