# Concepts for developers

> Understand how projects, elements, types, entries, groups and drafts fit together, and how the read-only Content Delivery API serves only published content.

Spatibase stores map content as types made of elements (fields), filled in as entries, and serves only the published copy through a read-only Content Delivery API. This page explains each piece for developers, using one example throughout: a map of coffee shops.

## How does the Spatibase model fit together?

A project holds reusable elements, types built from those elements, entries of those types, and hand-picked groups of entries. Only the published copy of each entry reaches your app.

```text
Project "Prague Coffee"                      one UUID, one API address
 |
 |-- Elements (reusable fields)              description, photos, rating, opens-at ...
 |
 |-- Types (what an entry is)                an ordered list of elements + map settings
 |     Venue                                 locationMode: required, color #e11d48
 |       |-- Cafe          (subtype)         inherits all Venue elements, adds its own
 |       `-- Roastery      (subtype)
 |
 |-- Entries (the content itself)            "Bean There" (Cafe) + values + main location
 |     draft copy  --ready + publish-->  published copy   <- only this reaches the Content Delivery API
 |
 `-- Groups (hand-picked, ordered lists)     "Featured on homepage"
```

## What is a project?

A project is the container for all content of one map or site, identified by a UUID that appears in every API URL. Find it in the web app's address bar (`https://app.spatibase.com/projects/<id>/entries`) or on the project's **API** page, which also shows ready-made URLs for that project.

```text
https://api.spatibase.com/delivery/v1/projects/$PROJECT_ID
```

`$PROJECT_ID` is the UUID in the project's address. A project's Content Delivery API is public by default; the owner can make it private so that every request needs an API key ([Authentication](https://spatibase.com/docs/reference/authentication/)).

## What are elements and element kinds?

An element is a reusable field, defined once per project and assigned to any number of types. Each element has a label, a stable `apiKey` (the key in API responses, for example `rating`) and a kind that decides what it holds and how you can filter it.

| Kind | Holds | Value in the API |
|---|---|---|
| `text`, `markdown`, `identifier` | text, rich text, a unique slug | string (text with the JSON editor: the parsed JSON) |
| `number`, `boolean`, `color` | a number, yes or no, `#rrggbb` | number, boolean, string |
| `date` | a date, date-time or time, or a range | string, or `{start, end}` for ranges |
| `choice` | one or several options (option trees up to 4 levels) | string or array of strings |
| `link`, `contact`, `location`, `embed` | structured values | objects |
| `phone` | a phone number as typed | string |
| `images`, `files` | uploaded assets | array of asset objects |
| `reference` | another entry, or several | `{id, type, title}`, or the full entry with `include=references` |
| `geo` | extra shapes (points, lines, areas) | GeoJSON FeatureCollection |

Values are always keyed by `apiKey`. When someone renames an apiKey in the web app, Spatibase rewrites the stored values to the new key, so update your code at the same time. The exact JSON shape of every kind is in [Field values](https://spatibase.com/docs/reference/field-values/).

## What are types and subtypes?

A type, such as "Cafe", is an ordered list of assigned elements plus map settings. Every entry belongs to exactly one type.

- `locationMode`: `required` (marking ready needs a main location), `optional`, or `none` (pure content such as articles; these entries never appear on the map, in GeoJSON or in vector tiles).
- `geometryKinds`: which shapes the main location may be: `Point`, `LineString`, `Polygon`.
- `color`: the type's color (**Type color** in the app), carried into vector tiles as the `color` property.

Types form a tree up to 5 levels deep. A subtype inherits every element of its ancestors and can add its own. Asking for a parent type returns its subtypes too: `type=venue` also returns cafes and roasteries. Every entry still reports its own `type` and a `typePath` such as `["venue", "cafe"]`.

Switching a type off in the content model hides the type, its subtypes and all their entries from the Content Delivery API until it is switched on again. Working with subtypes in code is shown in [Recipes](https://spatibase.com/docs/build/recipes/#work-with-subtypes).

## What is an entry and its main location?

An entry is one record of a type: a `title`, `fields` keyed by element apiKey, and an optional main location. The main location (`geometry`) is a GeoJSON `Point`, `LineString` or `Polygon` in WGS84, with coordinates in `[lng, lat]` order.

The main location drives the map: it is what `bbox`, `near` and `sort=distance` search, what `entries.geojson` returns as the feature geometry, and what vector tiles draw. Extra shapes, such as a delivery area or a parking lot, live in `geo` elements and never affect spatial filters.

```json
{
  "id": "9dc8c38c-5d1e-4a51-9c3b-1f0e2a7d6b10",
  "type": "cafe",
  "typePath": ["venue", "cafe"],
  "title": "Bean There",
  "geometry": { "type": "Point", "coordinates": [14.4208, 50.0875] },
  "fields": { "description": "Small **coffee** bar.", "rating": 4.7 },
  "groups": ["featured"],
  "createdAt": "2026-10-01T09:12:44.000Z",
  "updatedAt": "2026-10-02T14:03:10.000Z",
  "publishedAt": "2026-10-02T14:03:10.000Z"
}
```

## What is a group?

A group is a named, manually ordered list of entries of any type, for example "Featured on homepage" or one list per website. Editors set the order by hand. Read a group with `/groups/<groupKey>`, or combine it with filters through `/entries?group=<groupKey>&sort=group`.

Group changes go live with the next publish, like entries: the Content Delivery API shows the group as it was at the last publish. A group only ever lists published entries of enabled types, so an unpublished member simply does not appear.

## What are map feature types?

Map feature types are named, styled categories of map shape, such as "Path", "Trailhead" or "Park area", each with a geometry kind, an icon and a color. A `geo` element can restrict its shapes to some of them; each shape then carries `properties.featureType` with the feature type's apiKey.

List them at `/feature-types`:

```json
{"apiKey":"path","name":"Path","geometryKind":"LineString","icon":"route","color":"#b45309"}
```

Lengths and areas shown in the web app's map popups are not part of the API response.

## How do draft and published copies work?

Editors always work on a draft. An editor marks a draft **ready**, and a project-wide publish copies every ready draft into a separate published copy, after checking required fields, together with staged unpublishes and removals and group changes. One publish is one numbered version of everything live, and any kept version can be restored (see [Versions and going back](https://spatibase.com/docs/publish/versions/)). Unpublishing removes the published copy at the next publish. The Content Delivery API reads only the published copy, so saving a draft never changes what your app sees.

| Entry status | Meaning | In the Content Delivery API |
|---|---|---|
| `draft` | never published, or unpublished | not returned; `404` by id |
| `published` | published, no edits since | returned |
| `changed` | published, with newer unpublished edits | the last published version |

Your app never needs to filter drafts out, and cannot see them even by mistake. The editor's side of this is described in [Draft and published](https://spatibase.com/docs/publish/draft-and-published/).

## How do content version and caching work?

Each project has a `contentVersion` integer that increases whenever Content Delivery API output can change: a publish that ships something (which covers unpublishing, deleting a published entry and group changes), going back to an earlier version, an asset change or a content model change. The version numbers of the Versions page are different: they count publishes only. Saving a draft does not change it.

Every Content Delivery API response carries an `ETag` that includes the content version, and public projects send:

```http
Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=86400
```

Send the ETag back in `If-None-Match` to get a bodiless `304 Not Modified`. Browsers and CDNs can therefore cache aggressively and still pick up a publish within about a minute in browsers and five minutes on a CDN. Details are in [Content Delivery API: caching](https://spatibase.com/docs/reference/delivery-api/#caching), with a client example in [Recipes](https://spatibase.com/docs/build/recipes/#cache-with-etag-and-304).

## Which API should I use: Delivery or Management?

Use the Content Delivery API for websites, apps and maps that show published content. Use the Content Management API only when you need to change content from a script, because it works on drafts and the content model and needs a signed-in session.

| | Content Delivery API | Content Management API |
|---|---|---|
| Base URL | `https://api.spatibase.com/delivery/v1/projects/$PROJECT_ID` | `https://api.spatibase.com/api/projects/$PROJECT_ID` |
| For | your websites, apps and maps | the Spatibase web app and scripts that edit content |
| Methods | `GET` only, CORS open to any origin | read and write |
| Access | public, or an API key for private projects | a signed-in session (emailed sign-in link or code) |
| Content | published only | drafts and published |
| Caching | ETag and `304`, CDN-friendly | none |
| Versioning | versioned (`v1`) | not versioned; it follows the web app and can change |
| Machine-readable spec | [OpenAPI 3.1 files](https://spatibase.com/docs/reference/delivery-api/#machine-readable-spec): one typed for your project, one generic | none |

Asset files (images and other uploads) are served from `https://api.spatibase.com/assets/<assetId>/<filename>` and stay reachable by their URL even in private projects ([Assets and images](https://spatibase.com/docs/reference/assets-and-images/)). AI assistants use a third entry point, the MCP server ([MCP server](https://spatibase.com/docs/ai/mcp-server/)).

## Related

- [Quickstart: a map in ten minutes](https://spatibase.com/docs/build/quickstart/)
- [Content Delivery API reference](https://spatibase.com/docs/reference/delivery-api/)
- [Field values](https://spatibase.com/docs/reference/field-values/)
- [Content Management API](https://spatibase.com/docs/reference/management-api/)
- [Glossary](https://spatibase.com/docs/help/glossary/)
