> For the complete documentation index, see [llms.txt](https://docs.fylr.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fylr.io/for-developers/customdata.md).

# Custom Data

Custom Data Types allow the definition of custom types. The data of such a type is stored as JSON by FYLR.

All custom data is stored in its own table in FYLR, each record is stored only once per `_uuid`. The `_uuid` can be provided or will be auto-generated by FYLR based on the data which was sent. If that data changes a new UUID is generated and a new record is stored. The old record is kept in the database as long as it is references by any object (including historical versions).

## Providing UUID

Custom data can contain a `_uuid` at the top level of the data. With such an id FYLR is not generating one.

> Record uniqueness is determined soley on the provided UUID. So sending different data with the same UUID will not update the stored data. To update the stored data, a new UUID must be provided.

## Search

Custom Data Types support search mappings. The search mapping is defined in the `manifest.yml` of the plugin which defines the Custom Data Type, under `custom_types.<type>.mapping`. Each entry maps a subfield of the custom data to a column type:

```yaml
custom_types:
    example:
        mapping:
            title:
                type: text
            identifier:
                type: text_oneline
                skipTerms: true
```

The `type` of a subfield controls how its value is indexed in OpenSearch (for search and facets) and how it is broken into terms for the suggest term tables.

### skipTerms

> Available since FYLR 6.34.0.

Set `skipTerms: true` on a subfield to keep it **out of the suggest term tables** while it stays indexed in OpenSearch. The value remains searchable and usable for facets, but it no longer contributes terms to `/api/suggest`.

This is useful for subfields whose values would otherwise flood the suggest index — for example identifiers or URLs, which are split into many short fragments when broken into terms.

`skipTerms` affects only the term generation of that one subfield; it does not change how the subfield is indexed for search. It is independent of the instance-wide `fylr.debug.skipTerms` setting, which disables term generation for the whole instance.

## Updates

Custom Data Types can be updated by a background service provided by FYLR. This background service collects all expired custom data and asks a script of the plugin to provide any updates on that data as needed. The updates happen periodically and the Custom Data Type can provide an expiration date for each record.

The update script is declared under `custom_types.<type>.update` in the plugin's `manifest.yml` and receives the [callback contract](/for-developers/plugin/callbacks/contract.md) fields to call back into the API. A scheduled run has no acting user, so `api_user_access_token` is present only when the run is started from a request. To give the script a user of its own, `update.plugin_user.reference` names a fixed user by its `reference` — for example `system:root` — and the script receives `plugin_user` and `plugin_user_access_token` for that user:

```yaml
custom_types:
  example:
    update:
      plugin_user:
        reference: system:root   # the user's `reference`; easydb and system users only
      exec: { … }
```

A reference that matches no user is logged as a warning and the run continues without a plugin user.
