# Field values

> Look up the JSON each of the 17 element kinds returns in Delivery API output, how empty values appear and which filter operators work per kind.

Every entry's `fields` object has one key per element (field) of its type, keyed by the element's `apiKey`. This page gives the JSON shape, empty behaviour and filter operators of all 17 element kinds as the Content Delivery API returns them. Query syntax is in [Content Delivery API: filters](https://spatibase.com/docs/reference/delivery-api/#filters).

## Summary of all element kinds

Each element kind has one fixed JSON shape in Content Delivery API output, and every kind uses `null` for empty.

| Kind | JSON in Delivery output | Filter operators |
|---|---|---|
| [`text`](https://spatibase.com/docs/reference/field-values/#text) | string; with the JSON editor: the parsed JSON value | `eq`, `ne`, `in`, `contains`, `exists`; with the JSON editor: `exists` |
| [`markdown`](https://spatibase.com/docs/reference/field-values/#markdown) | string (Markdown, asset URLs absolute) | `eq`, `ne`, `in`, `contains`, `exists` |
| [`number`](https://spatibase.com/docs/reference/field-values/#number) | number | `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `exists` |
| [`boolean`](https://spatibase.com/docs/reference/field-values/#boolean) | `true` or `false` | `eq`, `ne`, `exists` |
| [`date`](https://spatibase.com/docs/reference/field-values/#date) | string (`YYYY-MM-DD`, ISO date-time or `HH:MM`); range: `{start, end}` | `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `exists`; range: `exists` |
| [`choice`](https://spatibase.com/docs/reference/field-values/#choice) | string; multiple: array of strings | `eq`, `ne`, `in`, `exists` |
| [`link`](https://spatibase.com/docs/reference/field-values/#link) | `{url, label?}` | `eq`, `ne`, `in`, `contains`, `exists` |
| [`images`](https://spatibase.com/docs/reference/field-values/#images-and-files) | array of asset objects | `exists` |
| [`files`](https://spatibase.com/docs/reference/field-values/#images-and-files) | array of asset objects | `exists` |
| [`contact`](https://spatibase.com/docs/reference/field-values/#contact) | `{name?, email?, phone?, website?, address?}` | `contains`, `exists` |
| [`color`](https://spatibase.com/docs/reference/field-values/#color) | string `#rrggbb` | `eq`, `ne`, `in`, `exists` |
| [`reference`](https://spatibase.com/docs/reference/field-values/#reference) | `{id, type, title}`; multiple: array of them | `eq`, `ne`, `in`, `exists`; multiple: `eq`, `in`, `exists` |
| [`geo`](https://spatibase.com/docs/reference/field-values/#geo) | GeoJSON FeatureCollection | `exists` |
| [`identifier`](https://spatibase.com/docs/reference/field-values/#identifier) | string | `eq`, `ne`, `in`, `contains`, `exists` |
| [`embed`](https://spatibase.com/docs/reference/field-values/#embed) | `{url, provider, embedUrl}` | `exists` |
| [`location`](https://spatibase.com/docs/reference/field-values/#location) | `{lng, lat, address?}` | `exists` |
| [`phone`](https://spatibase.com/docs/reference/field-values/#phone) | string | `eq`, `ne`, `in`, `contains`, `exists` |

The web app's names for these kinds are listed in [Elements](https://spatibase.com/docs/model/elements/).

## Rules that apply to every kind

These rules hold for all element kinds in Content Delivery API output:

- **Published copy only.** Values change in the API when the entry is published, never when a draft is saved.
- **Every effective element is present.** Inherited elements come first, then the type's own, in model order (as in [`/types`](https://spatibase.com/docs/reference/delivery-api/#list-types)). A field without a value is `null`, never missing.
- **Empty means `null`.** An absent value, `null`, `""`, `[]` and `{}` all come out as `null`. `false` and `0` are values, not empty: `filter[entrance-fee][exists]=true` includes an entry with `"entrance-fee": 0`.
- **Hidden elements.** Archived elements and elements no longer assigned to the type are left out of `fields`. Their stored values are kept and reappear if the element is assigned again.
- **`exists` follows the output.** `exists=true` matches exactly the entries where the field is not `null`. `exists=false` and `ne` also match entries whose type does not have the element.
- **Languages.** In a project with [languages](https://spatibase.com/docs/reference/delivery-api/#languages), only `text` (not JSON text) and `markdown` values, and the labels of `choice` options, depend on the language you ask for (`locale`); a missing translation falls back to the default language. Every other kind is the same in all languages, and so is a choice's stored value.
- **Settings live in `/types`.** Units, option labels, allowed shapes and other settings are in `elements[].config` of `/types`, not in the value.

## text

A `text` field is a string. Single-line text is trimmed when saved; multi-line text keeps its line breaks. Minimum and maximum lengths are checked when saving and do not affect output.

```json
"opening-hours": "Mon-Fri 9:00-18:00\nSat 9:00-17:00\nSun 10:00-16:00"
```

With `config.editor: "json"`, the stored text is parsed and delivered as JSON: an object, array, number, string or boolean. If the stored text is not valid JSON, the field is `null`. Only `exists` is supported; it is true when the text parses to something other than `null`.

```json
"specs": {"trailMiles": 9, "dogs": true, "lots": ["Ecola Point", "Indian Beach"]}
```

Filters on plain text: `eq`, `ne` and `in` are exact and case-sensitive; `contains` is a case-insensitive substring, with `%` and `_` taken literally. `filter[subtitle]=forested headland above cannon beach` finds nothing if the stored text is capitalised, while `filter[subtitle][contains]=HEADLAND` finds it.

## markdown

A `markdown` (rich text) field is a Markdown string as written in the editor. Links to the project's own files in `[](...)`, `![](...)`, reference definitions (`[x]: ...`) and `src=` or `href=` attributes are rewritten to absolute URLs, so the text works on another site. Other text is unchanged.

```json
"description": "See the [trail map](https://api.spatibase.com/assets/350f422e-2ab1-41ed-b8d7-90a9939f2d2b/trail-map.pdf).\n\n<img src=\"https://api.spatibase.com/assets/d959ef72-8b3c-4f0e-a1d2-7c5e9b4f6a13/logo.svg\" alt=\"\">"
```

Filters behave as for plain text, on the raw Markdown source.

## number

A `number` field is a JSON number. The unit (for example `"USD"`), minimum, maximum and whole-number setting are in `config` (`unit`, `min`, `max`, `integer`), not in the value.

```json
"rating": 4.9, "entrance-fee": 0
```

Filter values must be numeric (`4.8`, `-1`, `1e3`), and comparison is numeric.

## boolean

A `boolean` field is `true` or `false`. Filter values must be `true` or `false`. `filter[open-now]=false` matches only entries that store `false`, not empty ones; use `filter[open-now][exists]=false` for those.

```json
"open-now": false
```

## date

A `date` field is the stored string in a form set by `config.mode`:

| Mode | Value | Example |
|---|---|---|
| `date` (default) | `YYYY-MM-DD` | `"founded": "1932-05-01"` |
| `datetime` | ISO 8601 date-time as saved. The web app saves UTC (`...Z`); imports and the Content Management API keep the offset they were given | `"next-event": "2026-11-07T18:00:00.000Z"`, `"next-event": "2026-11-07T10:00:00-08:00"` |
| `time` | `HH:MM` or `HH:MM:SS`, 24-hour, no date or zone | `"opens-at": "09:00"` |

With `config.range: true` the value is `{start, end}` in the same form. Key order in the JSON is not meaningful. A range can only be filtered with `exists`.

After a mode switch the value follows the field's current mode; stored values are unchanged. In `date` mode a stored date-time gives its date (the first 10 characters, no time zone conversion). A value that doesn't fit the mode (a date or date-time in `time` mode, a time in `date` or `datetime` mode) is `null`; a date without a time stays valid in `datetime` mode. A range applies this to both ends and is `null` if either end doesn't fit.

```json
"season": {"start": "2027-05-01", "end": "2027-10-15"}, "visiting-hours": {"start": "06:00", "end": "22:00"}
```

Filters on single values compare typed values, not strings:

- **`date`** compares calendar dates. A stored or given date-time is reduced to its date, so `filter[founded]=1953-07-01` matches `"1953-07-01T12:00:00Z"`.
- **`datetime`** compares instants. `filter[next-event]=2026-11-07T18:00:00Z` and `filter[next-event]=2026-11-07T19:00:00%2B01:00` match the same entries. Always send `Z` or an offset, and encode `+` as `%2B`: an unencoded `+` becomes a space and the request fails with `400`.
- **`time`** compares times of day. `filter[opens-at][lt]=09:30` and `filter[opens-at][lt]=9:30` are the same; `25:00` is a `400`.

## choice

A `choice` field is the chosen option's value, not its label. With `config.multiple: true` it is an array of distinct values in the order chosen. Options can form a tree up to 4 levels deep, and any node, parent or child, can be chosen; labels and the tree are in `config.options` of `/types`.

```json
"difficulty": "easy", "amenities": ["restrooms", "parking", "camping", "visitor-center"]
```

- Filter values must be option values of the element; the `400` lists them. Matching is exact, so a parent option does not match its children: `filter[activities]=hiking` does not find `day-hike`.
- For multiple choice, `eq` means "has this option", `in` means "has any of them", and `ne` means "does not have it". Two `eq` filters on the same key need both options.
- The shape follows the element's current setting: after multiple choice is switched on, a value saved earlier comes out as a one-item array; after it is switched off, a list comes out as its first known option.
- Only current options count. A value that is no longer an option (removed or renamed while entries used it) comes out as `null` for a single choice and is dropped from a multiple choice (`null` when nothing is left); `filter[<key>][exists]` matches the same way. The stored value is kept, so it comes back if the option does. Vector tiles follow the same rule.

## link

A `link` field is `{url}` or `{url, label}`; the label is left out when empty. URLs are `http` or `https`.

```json
"website": {"url": "https://www.nps.gov/redw/", "label": "National Park Service"}
```

`eq`, `ne` and `in` compare the URL exactly; `contains` searches the URL and the label, case-insensitively.

## images and files

An `images` or `files` field is an array of asset objects in the order the editor arranged them. `images` only accepts web images (JPEG, PNG, GIF, WebP, AVIF, SVG); `files` accepts any file, optionally limited by `config.allowedFormats` (for example `[".pdf"]`). Assets deleted from the media library are dropped, and when none are left the field is `null`.

```json
"brochure": [{"id": "350f422e-2ab1-41ed-b8d7-90a9939f2d2b", "url": "https://api.spatibase.com/assets/350f422e-2ab1-41ed-b8d7-90a9939f2d2b/trail-map.pdf", "filename": "trail-map.pdf", "mimeType": "application/pdf", "size": 125, "width": null, "height": null, "alt": ""}]
```

| Key | Meaning |
|---|---|
| `id` | the asset's UUID |
| `url` | absolute file URL; images accept resize parameters ([Assets and images](https://spatibase.com/docs/reference/assets-and-images/)) |
| `filename`, `mimeType` | the file name and media type |
| `size` | size in bytes |
| `width`, `height` | pixels for images, as displayed (after EXIF rotation); `null` for other files |
| `alt` | alt text from the media library, `""` if none |

Only `exists` is supported.

## contact

A `contact` field is an object with only the non-empty keys among `name`, `email`, `phone`, `website` and `address`. `address` is itself an object with any of `street`, `city`, `region`, `postalCode` and `country`.

```json
"contact": {"name": "Arcata store", "email": "arcata@example.com", "phone": "+1 707-555-0134", "website": "https://example.com/arcata", "address": {"street": "855 G Street", "city": "Arcata", "region": "CA", "postalCode": "95521", "country": "United States"}}
```

`contains` searches all parts at once (`filter[contact][contains]=arcata`); `eq` is not supported.

## color

A `color` field is a lowercase hex color, `#rrggbb`; input such as `#16A34A` is stored lowercase. Filter values must be `#rrggbb` and match case-insensitively. Encode `#` as `%23` in URLs: `filter[brand-color]=%2316a34a`.

```json
"brand-color": "#16a34a"
```

## reference

A `reference` field is a stub of the linked entry, `{id, type, title}`, using the target's published title and type apiKey. With `config.multiple: true` it is an array of stubs. `include=references` replaces stubs with full entry objects, one level deep ([Expand references](https://spatibase.com/docs/reference/delivery-api/#expand-references)).

```json
"region": {"id": "66f36784-bb65-4de3-a745-bfd294cdf017", "type": "region", "title": "Northern California Coast"}, "parks": [{"id": "822eecd4-c73c-4bc2-8289-6232512259eb", "type": "national-park", "title": "Redwood National Park"}]
```

- A target that is unpublished, deleted or of a switched-off type is hidden: a single reference becomes `null`, a multiple reference drops it, and an empty list becomes `null`. Filters see the same thing: `exists`, `eq` and `in` only match visible targets.
- Filter values are entry ids (UUIDs). `filter[region]=<regionId>` finds everything that links to one entry, such as all stores and parks in a region. Multiple references support `eq` (links to this entry) and `in`, but not `ne`.
- `config.targetTypes` in `/types` lists the allowed target types; their subtypes are allowed too.

## geo

A `geo` (map shapes) field is a GeoJSON FeatureCollection with one or more shapes, independent of the entry's main location. Each feature has a `geometry` (`Point`, `LineString`, `Polygon` or a `Multi*` form, `[lng, lat]`, 7 decimals) and `properties` with an optional `label` and, when the element uses map feature types, a `featureType` apiKey. Names, icons and colors of feature types come from [`/feature-types`](https://spatibase.com/docs/reference/delivery-api/#list-map-feature-types). No shapes means `null`.

```json
"parking": {"type": "FeatureCollection", "features": [{"type": "Feature", "geometry": {"type": "Point", "coordinates": [-124.0912, 41.2843]}, "properties": {"label": "Kuchel Visitor Center", "featureType": "parking"}}]}
```

Geo fields are not used by `bbox` or `near` and are not in vector tiles. Only `exists` is supported; it is true when there is at least one shape.

## identifier

An `identifier` field is a string of letters, digits, `.`, `_` and `-` (at most 64 characters by default), unique per element across the project's entries. Use it for slugs and external ids. Filters work as for plain text: `filter[slug]=redwood-national-park`.

```json
"slug": "redwood-national-park"
```

## embed

An `embed` field is `{url, provider, embedUrl}`: the pasted URL, the detected provider, and an iframe-ready URL. `config.providers` may restrict which providers editors can use.

| `provider` | `embedUrl` |
|---|---|
| `youtube` | `https://www.youtube-nocookie.com/embed/<id>` |
| `vimeo` | `https://player.vimeo.com/video/<id>`, with `?h=<hash>` for unlisted videos |
| `spotify` | `https://open.spotify.com/embed/<track, album, playlist, episode, show or artist>/<id>`, or `null` for other Spotify pages |
| `soundcloud` | `https://w.soundcloud.com/player/?url=<encoded track URL>` |
| `other` | `null`; link to `url` instead |

```json
"video": {"url": "https://www.youtube.com/watch?v=HkCrhLf-ksY", "provider": "youtube", "embedUrl": "https://www.youtube-nocookie.com/embed/HkCrhLf-ksY"}
```

`embedUrl` is also `null` when no player address can be built from the URL, for example a YouTube playlist or a SoundCloud home page. Only `exists` is supported.

## location

A `location` field is a single point with an optional address: `{lng, lat}` or `{lng, lat, address}`. Longitudes are wrapped into -180 to 180 and both values are rounded to 7 decimals. This is a field, not the entry's main location, so it is not used by `bbox`, `near`, GeoJSON geometry or tiles.

```json
"starting-point": {"lng": -124.0912, "lat": 41.2843, "address": "Kuchel Visitor Center, Orick, CA 95555"}
```

Only `exists` is supported.

## phone

A `phone` field is the number as typed, trimmed and with runs of spaces collapsed: digits, spaces and `+ ( ) . - /`, 5 to 25 characters with at least 5 digits. It is not normalised to a standard format, so `contains` on the digits you know is the most reliable filter: `filter[rental-phone][contains]=707`.

```json
"rental-phone": "+1 707-555-0135"
```

## Related

- [Content Delivery API reference](https://spatibase.com/docs/reference/delivery-api/)
- [Elements](https://spatibase.com/docs/model/elements/)
- [Assets and images](https://spatibase.com/docs/reference/assets-and-images/)
- [GeoJSON and vector tiles](https://spatibase.com/docs/reference/geojson-and-tiles/)
