> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usenexio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Application records

> Understand how Nexio-hosted applications keep their own per-user records and files in the platform, and why outside callers cannot reach them.

An application built on Nexio needs somewhere to keep its own state: a saved view, a draft, a checklist, an uploaded file. The app data store holds that state in Nexio, next to the organization's data, under the same tenancy rules. It is separate from the connected system of record: nothing here is read from or written to that system.

<Warning>
  The app data store is available to Nexio-hosted applications only. Its routes (`/api/v1/app-store/*`) admit only the organization's live API key, together with an acting principal. Scoped keys get 403 `insufficient_capability`. Sandbox and test organization keys get 403 `scoped_key_required`. This page explains the model so you know what those applications store. It is not an integration surface.
</Warning>

## How it works

1. A Nexio-hosted application's server holds the organization's live key. It sends the signed-in person as `X-Nexio-Acting-Principal` on every call. A call without it answers 401 `unauthorized`.
2. The application is one of a fixed list of six Nexio-hosted applications, set in code; no organization adds one. An unknown application answers 404 `not_found`.
3. Inside an application, records live in named collections. A collection needs no setup: its name must match `^[a-z0-9][a-z0-9_-]{0,62}$`.
4. Every write is owned by the acting principal. A read returns the caller's own record, or a shared record with the same id. A person never reads another person's owned records.
5. Files are not stored inside records. They go through a separate blob flow with short-lived upload and download grants.

## Records

A record (a row) has:

* `id`: the caller's identifier, 1 to 200 bytes, no `/` and no control characters.
* `subjectKeys`: up to 16 promoted string fields the application filters on, for example `{"account": "acct-4471"}`. Key pattern `^[a-z][a-z0-9_]{0,63}$`, each value 1 to 500 bytes.
* `payload`: any JSON value except null, up to 256 KiB. A payload that embeds file bytes (a `data:` URL, or a key such as `base64` or `contentBase64`) is refused. Files use blobs.
* `version`: a counter the server owns. Create with `version: 0`. Each change, including a delete, increments it.
* `ownerPrincipal`: the owner, or null for a shared record. Shared records are created only by Nexio's import path.

```json A record theme={null}
{
  "id": "review-checklist-acct-4471",
  "ownerPrincipal": "user_01J8Z3K4M5N6P7Q8R9S0T1U2V3",
  "subjectKeys": { "account": "acct-4471" },
  "payload": { "items": [{ "label": "Request security questionnaire", "done": true }] },
  "version": 3,
  "createdAt": "2026-09-20T15:04:05Z",
  "updatedAt": "2026-09-23T09:12:44Z",
  "deletedAt": null
}
```

Rules the store enforces:

* **Optimistic concurrency.** A write names the version it expects. A stale version answers 409 `version_conflict`. Retrying the same write with the same content returns the stored record without incrementing, so a retry after a timeout is safe.
* **Batch writes.** One request writes 1 to 100 records in a single transaction. All succeed or none do.
* **Soft delete.** A delete names the expected version and marks the record deleted. `recreate: true` with `version: 0` revives a deleted id.
* **Paging.** Lists sort newest first by `updatedAt`. The cursor is signed, carries a read pin, and expires after one hour. Filters are `subject.<key>=<value>` (at most 100 values in total), `updated_since` (RFC 3339) and `limit`.

## Files (blobs)

Blob states are `pending`, `ready` and `rejected`, plus soft delete.

1. **Reserve.** The application sends `id`, `name`, `contentType` and `sizeBytes`. The store answers 201 with an upload URL valid for 15 minutes.
2. **Upload.** The application uploads the bytes directly to that URL, without the Nexio key.
3. **Finalize.** The store checks the uploaded object. A size mismatch answers 409 `upload_mismatch`. A file over 10 MiB answers 413 `object_too_large`. A type that does not match its declared media type answers 415 `unsupported_media`. On success the blob is `ready`.
4. **Read.** Metadata is served for `ready` blobs only. The content route returns a download URL valid for 5 minutes. PNG and JPEG open inline; every other type downloads as an attachment.
5. **Delete.** Soft-deletes the metadata.

Accepted media types: `application/json`, `application/pdf`, `image/png`, `image/jpeg`, `image/webp`, `image/svg+xml`, `text/csv`, and Excel workbooks (`.xlsx`). When the deployment has no object store configured, every blob route answers 503 `storage_unavailable`.

## Limits

| Limit                   | Value                                           |
| ----------------------- | ----------------------------------------------- |
| Page size               | default 50, maximum 200 (a larger value is 400) |
| Records per batch write | 1 to 100                                        |
| Payload                 | 256 KiB                                         |
| Subject keys per record | 16 (16 KiB serialized)                          |
| Subject value           | 500 bytes                                       |
| Filter values per list  | 100                                             |
| Record id               | 1 to 200 bytes                                  |
| Single write body       | 300 KiB                                         |
| Batch write body        | 1 MiB                                           |
| Cursor lifetime         | 1 hour                                          |
| Blob size               | 1 byte to 10 MiB                                |
| Blob name               | 255 bytes                                       |
| Upload grant            | 15 minutes                                      |
| Download grant          | 5 minutes                                       |

## Errors

| Status | Code                                    | Cause                                                                                |
| ------ | --------------------------------------- | ------------------------------------------------------------------------------------ |
| 400    | `invalid_request`                       | A malformed body, name, filter or limit.                                             |
| 400    | `invalid_cursor`                        | The cursor is malformed, tampered with or expired.                                   |
| 401    | `unauthorized`                          | No organization key, or no acting principal.                                         |
| 403    | `insufficient_capability`               | A scoped key. The store does not admit scoped keys.                                  |
| 403    | `scoped_key_required`                   | A sandbox or test organization key.                                                  |
| 403    | `forbidden`                             | The record or blob belongs to someone else, or the list asked for another owner.     |
| 404    | `not_found`                             | Unknown application, record or blob.                                                 |
| 409    | `version_conflict`                      | The expected version is stale.                                                       |
| 409    | `blob_conflict`, `upload_mismatch`      | A different reservation already holds the id, or the uploaded bytes do not match it. |
| 413    | `request_too_large`, `object_too_large` | The body or the file is over its limit.                                              |
| 415    | `unsupported_media`                     | The file type is not accepted.                                                       |
| 503    | `storage_unavailable`                   | No object store is configured.                                                       |

## Next

<CardGroup cols={2}>
  <Card title="Records overview" href="/data-services/overview">The read and write layer these applications build on.</Card>
  <Card title="Authority and scope" href="/data-services/scope">How the acting principal works.</Card>
</CardGroup>
