Flex Objects
Endpoints for managing Flex Object directories and their records: listing directories and blueprints, full CRUD on objects, YAML export, and per-object media (files stored alongside the object in its own folder).
Endpoints for managing Flex Object directories and their records: listing directories and blueprints, full CRUD on objects, YAML export, and per-object media (files stored alongside the object in its own folder).
Get Flex Config
/flex-objects/config
{"data": {"enabled": true, "built_in_css": true, "security": {"restrict_page_frontmatter": true}, "admin_list": {"per_page": 15, "order": {"by": "updated_timestamp", "dir": "desc"}}}}
Response Codes
Lightweight configuration endpoint. The list of directories is returned separately by GET /flex-objects so callers that only need config stay small.
List Directories
/flex-objects
{"data": [{"type": "contacts", "title": "Contacts", "description": "Address book", "icon": "fa-address-book", "list": {"fields": {"name": {}, "email": {}}}, "edit": {}, "search": {"fields": ["name", "email"]}, "field_types": {"name": "text", "email": "email"}, "field_options": {}, "export": {}}]}
Response Codes
Each entry's type is the Flex directory key you pass as {type} to the object endpoints below. Directories the user lacks list permission on are omitted from the result, as are directories whose blueprint has no admin section or sets admin.disabled. Each entry has the same fields as Get Directory Metadata.
List Blueprints
/flex-objects/blueprints
{"data": [{"url": "blueprints://flex-objects/contacts.yaml", "legacy_url": null, "type": "contacts", "title": "Contacts", "description": "Address book"}]}
Response Codes
legacy_url carries the pre-existing blueprint alias (when one exists) so saved settings that still reference the old form can be matched.
List Objects
/flex-objects/{type}
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory type (e.g. `contacts`). |
| page optional | integer | Page number for pagination (default 1). |
| per_page optional | integer | Number of results per page. Defaults to the directory's `admin.list.options.per_page`, else the API default (20); capped at 1000. |
| search optional | string | Search term applied across the directory's searchable fields. |
| filters optional | object | Exact-match field filters, as `filters[field]=value` query params or a JSON object string. An array value matches any of its values; `key` or `id` matches the object key. |
| sort optional | string | Field to sort by. Defaults to the directory's `admin.list.options.order.by`, else unsorted. |
| order optional | string | Sort direction: `asc` or `desc`. Defaults to the directory's configured order direction, else `asc`. |
{"data": [{"key": "ada", "name": "Ada Lovelace", "email": "[email protected]"}], "meta": {"pagination": {"page": 1, "per_page": 20, "total": 1, "total_pages": 1}}, "links": {"self": "https://example.com/api/v1/flex-objects/contacts?page=1&per_page=20"}}
Response Codes
The key on each item is the object identifier you pass as {key} to the single-object endpoints. A directory with no configured list fields returns each object's full data instead. When the directory configures a related detail list (admin.list.detail) that the user can see, each item also carries a __detail object describing the related directory, the filter that selects its records, and whether the user can edit or delete them.
Get Object
/flex-objects/{type}/{key}
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory type (e.g. `contacts`). |
| key required | string | The object key. |
{"data": {"key": "ada", "__meta": {"type": "contacts", "key": "ada", "storageKey": "ada", "storagePath": "user-data://flex-objects/contacts/ada"}, "name": "Ada Lovelace", "email": "[email protected]"}}
Response Codes
Keep the returned ETag and send it back as an If-Match header when updating, so a concurrent change is detected instead of silently overwritten.
The reserved __meta object is read-only information for the admin (the object's type, key, storage key and, when known, its storage folder). It is never saved: create and update strip it from the request body.
Create Object
/flex-objects/{type}
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory type (e.g. `contacts`). |
{"name": "Grace Hopper", "email": "[email protected]"}
{"data": {"key": "grace-hopper", "__meta": {"type": "contacts", "key": "grace-hopper", "storageKey": "grace-hopper"}, "name": "Grace Hopper", "email": "[email protected]"}}
Response Codes
Post to the collection address (no key segment). To attach files to the object afterwards, use the Upload Object Media endpoint below with the key returned here.
Update Object
/flex-objects/{type}/{key}
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory type (e.g. `contacts`). |
| key required | string | The object key. |
{"email": "[email protected]"}
{"data": {"key": "grace-hopper", "__meta": {"type": "contacts", "key": "grace-hopper", "storageKey": "grace-hopper"}, "name": "Grace Hopper", "email": "[email protected]"}}
Response Codes
This is a partial update: fields you omit are left untouched. Use PATCH, not POST (posting to an object URL is not allowed).
If the body sends a file field (file, avatar, pagemedia, or any field with a destination) without a file it previously held, that file is deleted from the object's own folder when the object saves. Files stored in a shared destination such as media:// are only unlinked from the field and stay on disk.
Delete Object
/flex-objects/{type}/{key}
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory type (e.g. `contacts`). |
| key required | string | The object key. |
Response Codes
Returns no content on success.
Export Directory
/flex-objects/{type}/export
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory type (e.g. `contacts`). |
Response Codes
Export only works for directories whose blueprint sets admin.export.enabled; the built-in user accounts, groups and pages directories don't, and return 404. Because it returns every field of every object, it needs the directory's read permission, not just list.
The response is application/x-yaml with a filename like contacts-YYYY-MM-DD.yaml, not the standard JSON envelope.
List Object Media
/flex-objects/{type}/{key}/media
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory type (e.g. `contacts`). |
| key required | string | The object key. |
{"data": [{"filename": "avatar.png", "type": "image/png", "size": 20480, "url": "/user/data/flex-objects/contacts/ada/avatar.png"}]}
Response Codes
Object media requires folder-based storage (one folder per object, e.g. user-data://flex-objects/contacts/{id}). Directories that keep all records in a single shared file have nowhere to store per-object files, and the endpoint returns 422.
Upload Object Media
/flex-objects/{type}/{key}/media
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory type (e.g. `contacts`). |
| key required | string | The object key. |
| file required | file | File(s) to upload (multipart/form-data). Nested fields like `file[]` are supported. |
{"data": [{"filename": "avatar.png", "url": "/user/data/flex-objects/contacts/ada/avatar.png", "type": "image/png", "size": 20480}]}
Response Codes
Send the file as a multipart/form-data request (set Content-Type: multipart/form-data), not JSON. Uploads are validated against Grav's dangerous-extensions list and a 64 MB size cap. Requires folder-based storage for the directory (one folder per object); directories that use single-file storage return 422.
A file field can pass its own upload settings as extra form fields: random_name, avoid_overwriting, accept and filesize (in MB). These only tighten the checks above; they can't raise the 64 MB cap.
Delete Object Media
/flex-objects/{type}/{key}/media/{filename}
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory type (e.g. `contacts`). |
| key required | string | The object key. |
| filename required | string | The media filename to delete. |
Response Codes
Returns no content on success.
Get Directory Metadata
/flex-objects/{type}/metadata
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory key. |
{"data": {"type": "contacts", "title": "Contacts", "description": "Address book", "icon": "fa-address-book", "list": {"fields": {"name": {}, "email": {}}}, "edit": {}, "search": {"fields": ["name", "email"]}, "field_types": {"name": "text", "email": "email"}, "field_options": {}, "export": {}}}
Response Codes
This is the same payload each entry of List Directories carries, fetched for a single directory. field_types and field_options let the client render typed list cells (dates as dates, choice values as their labels) instead of raw stored values.
Get Blueprint
/blueprints/flex-objects/{type}
Parameters
| Name | Type | Description |
|---|---|---|
| type required | string | The Flex directory key. |
{"data": {"name": "contacts", "title": "Contacts", "type": null, "child_type": null, "validation": "loose", "fields": [{"name": "name", "type": "text", "label": "Name"}, {"name": "email", "type": "email", "label": "Email"}]}}
Response Codes
This is the form definition (fields, types, labels, validation), not object data. It is what Admin Next requests to build the editor for a Flex object. Use Get Object for a record's values.