Declaration reference¶
The keys of an admin.py resource, in manifest version 1. See
Add your module for a complete example.
| Declaration | Meaning / constraints |
|---|---|
name, label |
Unique resource name and human menu/page label. |
group, icon |
Optional navigation section label and icon name (from the UI's fixed icon set; unknown names fall back to a generic icon). Presentation only. |
access |
Exactly one of authenticated: true or a nonempty roles allowlist. Visibility only; every API call is independently authorized. |
list |
Explicit GET path owned by this module under /api/v1/. Response is an array with string/integer id on every row. items_field names a top-level array in an envelope. |
pagination |
offset uses the named offset/limit with limit+1 lookahead; cursor uses its opaque query cursor and next_header; all is only for genuinely unpaged APIs. No invented COUNT or page tokens. max_page_size defaults to 100 and is checked against API limits. |
columns, show, labels |
Explicit read-field lists and optional human labels. A missing show list defaults to the columns. Nested/object read fields render as JSON. |
detail, create, edit, delete |
Operations are opt-in; only supported buttons/routes appear. Detail/edit/delete have one record path parameter. Create/edit return a JSON record with id. Forms specify an input allowlist in fields; full output records are never sent back. |
filters |
Names of actual list query parameters. Input types derive from their API schema; no undeclared filters are sent. |
sort_parameter, sort_fields |
Optional sorting only when the API explicitly enumerates both field and -field query values. Otherwise column sorting is disabled. |
actions |
Named record commands with explicit path/method, fields, label and mandatory confirmation text. Optional access further controls visibility. A successful command may have an empty/204 response. tone: "danger" styles it as destructive; typed_confirmation names a displayed (show) field the operator must retype; placement: "widget" hands rendering to a declared widget that knows live state (default "toolbar"). Every command also sends an Idempotency-Key header equal to its operation reference. Actions without idempotency_field are offered as bulk commands over selected rows (one request per record, key reference:id). |
audit |
Explicit {resource_type, id_field} identity for record Activity. The ID field must exist in the public detail DTO. No binding means no guessed record history. Subscription resources can use their PSP ID instead of local integer IDs. |
scope |
Required tenant UUID selector, sent as X-Tenant-Id; optional parameter also binds the tenant into each operation path. Navigation and query keys carry the scope; changing it resets drafts, selection and cursor state. |
show_widgets, edit_widgets |
IDs of compiled specialized components; no scripts, imports or HTML from the manifest are executed. show_widgets fills detail-page slots. edit_widgets renders the same compiled slots alongside the edit form. |
Supported inputs: string/email/UUID/date/datetime, boolean, integer/number, scalar
enum and nullable variants. Required fields, length/range, simple patterns and
multiples derive from DTO constraints; default false and 0 are preserved.
Write-only/read-only input properties, nested writes, ambiguous unions and
unsupported constraints fail export. Complex validation still belongs to the
server. Server 422 field errors are mapped back to the generated form.
An explicit record command looks like this:
"actions": [{
"name": "publish",
"label": "Publish",
"path": "/api/v1/notes/{note_id}/publish",
"method": "POST",
"fields": [],
"confirm": "Publish this note?",
}]
The module must actually provide and authorize that route. Commands and destructive
operations wait for server confirmation; no optimistic success or automatic
mutation retry is used. After a transport failure the effect may already have
committed: the UI says so, and offers only a retry with the SAME reference
(the Idempotency-Key), which an endpoint guarded by idempotency_guard
replays instead of re-applying. It does not invent DB-atomic deduplication for
endpoints that have none. An action may declare
idempotency_field naming a required UUID input. The renderer supplies a stable
random UUID per command, retains it after an uncertain failure, and rotates it
only after success. The API must still bind that key to its parameters and
commit its dedupe record with its effect (the bundled credit adjustment does).
This release does not implement arbitrary JSON Schema, relationship autocomplete, nested editors, uploads or export/import. Saved views (named filter sets) are per-operator browser preferences. Identity grants and customer billing navigation are compiled extensions over public APIs. A custom scoped resource may use the UUID selector; owner directories need no selected tenant.