Record types & records
Record types are your structured data models: typed fields, validation, optional backoffice visibility, and public REST endpoints consumed by generated React code.
Why record types
Instead of hard-coding static content only, apps load and save entities (leads, products, bookings) through Prowpt APIs. The AI can create types and sample rows while generating UI.
Fields and validation
Each field has a type (text, long text, number, boolean, date, email, select, multi-select, file, etc.) and optional constraints. Labels drive both backoffice forms and helper text in generated apps.
- Select options can be curated lists ideal for statuses and categories.
- File fields store uploads served from your project asset space (ZIP backups include binaries).
- Mark a type as the user-profile shape to align with app user management features.
Draft vs published schemas
Schema edits start as draft field definitions. Publishing activates the schema for live traffic while letting you stage breaking changes safely.
Records API shape
List endpoints return paginated payloads with items, total, limit, and offset. Use these from generated fetch helpers or custom hooks the assistant adds for you.
Record-level access policy (RBAC)
Every record type has an access_policy that declares the universal floor for who may perform each CRUD action. New record types default to all-four owner_only — the most restrictive option. The assistant opts into looser policies only when the use case requires it (e.g. public blog posts, anonymous contact forms). Edit the policy from the record type's settings page in the editor.
| Action | Allowed clauses | Meaning of each clause |
|---|---|---|
| read | public · any_authenticated · owner_only · deny | public = anyone (incl. anonymous) can list/read; any_authenticated = signed-in app users; owner_only = only the user who created the row; deny = no one (use with a role grant for moderator-only types). |
| create | public · any_authenticated · owner_only · deny | Same clauses as read. owner_only on create just means a Bearer is required so the new row has an owner; anonymous public creates are rate-limited to 5 req/min/IP. |
| update | any_authenticated · owner_only · deny | No public clause — updates always require auth. owner_only restricts to the row's creator. |
| delete | owner_only · deny | Strictest by design — only the owner can delete, or no one (delete via a role grant). |
Record ownership column
Every row carries records.app_user_id, the FK to the signed-in app user who created it. The platform manages this column for you — generated app code never needs to set, send, or filter by it.
- On POST /records/{slug} the backend stamps app_user_id from the verified Bearer token. Anonymous creates leave it NULL (only allowed when the create policy is public).
- Any caller-supplied app_user_id in the request body — flat or nested under "data" — is stripped server-side and a security event is recorded in the project's events log.
- On owner_only LIST reads the backend filters the response to the caller's own rows automatically. Generated frontends just call GET /records/{slug} with the Bearer; no client-side ownership filtering needed.
- On owner_only UPDATE / DELETE the backend compares the row's app_user_id against the caller and returns a generic 403 on mismatch (with a record_access_denied event in the log for the owner to inspect).
- Owner-side IDE writes (POST /api/projects/{id}/records/{slug}) honor app_user_id from the payload — by design, project staff need to be able to assign records to any app user.