---
title: Types & tags
category: concept
slug: types
summary: "Tags are how Droplets find each other. Free-form tags are short labels anyone can attach; Types are curated tags that carry typed-field schemas the system renders, filters, and sorts."
---
# Types & tags

Tags are how Droplets find each other. Free-form tags are short labels anyone can attach; Types are curated tags that carry typed-field schemas the system renders, filters, and sorts.

Tags are how Droplets find each other. Most tags are **free-form** — short labels (`onboarding`, `q3-2026`, `vendor-risk`) that anyone with edit permission can attach. A small curated set are **Types**, which carry a schema of structured fields the system understands.

## Tags (free-form)

A tag is a short string label on a Droplet. Anyone with edit permission can add or remove one. Tags aren't validated, they're not typed, and they don't carry extra information beyond the string itself. Most tags fit here — use them for whatever vocabulary your team finds useful.

The dashboard autocompletes against tags you and your collaborators have used. The search bar accepts `tag:onboarding` as a filter. Tags live on each Droplet as a normalized list (lowercased, trimmed, deduplicated).

## Types

A **Type** is a tag that's been *promoted* into the typed-field registry. Promotion attaches a **schema** to the tag name — a curated set of **structured fields** the system expects when a Droplet wears it. Per feat/044, Types live at one of three scopes (USER / SPACE / GLOBAL); the scope determines who can curate the row.

For example, the `recommendation` Type carries:

- `status` — one of `draft`, `in-flight`, `decided`, `blocked` (configurable per Type)
- `progress` — an integer 0–100 the system renders as a progress bar
- `priority` — one of `low`, `medium`, `high`, `urgent`
- `due` — a date the system renders as a countdown (or "Nd overdue")
- `trafficLight` — one of `red`, `yellow`, `green`
- `owner` — a user reference (renders as avatar + name)
- `rice` — a free-form numeric custom field for the RICE score

A Droplet wearing the `recommendation` Type has these fields available; their values render with their special widgets on the Droplet detail page, the `/view` strip below the Drop Bar, dashboard cards, and search results.

You don't pick "what type of Droplet this is" from a dropdown — you just add the Type to the Droplet like any other tag, and the schema kicks in. Removing the Type clears the structured fields; the Droplet's other tags stay put.

A Droplet can wear multiple Types. Fields from multiple type schemas combine additively; if two Types both declare a `status` field, the values are namespaced (`recommendation.status` vs `decision.status`) and rendered separately.

## Primitive fields

The system knows how to render a small registry of **primitive field types**. A Type's schema opts into any subset of these. Primitives can't be redefined — they're baked into the platform so that `progress` always means the same thing across every Type.

| Field | Type | Renders as |
|---|---|---|
| `status` | enum (per-tag values) | Coloured status pill |
| `progress` | int 0–100 | Progress bar with percent label |
| `priority` | enum: `low` / `medium` / `high` / `urgent` | Coloured swatch |
| `due` | date | Countdown (`in 12d`) or overdue indicator (`3d overdue`, coral) |
| `trafficLight` | enum: `red` / `yellow` / `green` | Coloured dot |
| `owner` | user-ref | Avatar + display name |

The registry is hardcoded in `lib/type-fields.ts` — adding a new primitive is a code change, not a runtime config. This is deliberate: the renderable set stays small and well-understood. Anything more exotic lives in custom fields.

## Type-bound custom fields

Beyond the primitives, a Type can declare custom fields of these basic types:

- `string` — short label, max 100 chars
- `int` — integer (e.g. `rice`, `impact-score`)
- `date` — ISO date
- `bool` — true / false
- `enum` — type-defined enum values
- `user-ref` — pointer to a @himarley user
- `user-list` — ordered list of users and/or user-groups (RAPID-style multi-principal). Stored as `[{kind:'user'|'group', id}, …]`. Group references resolve dynamically on read — if the group's membership changes, every Droplet referencing the group reflects the new membership without an edit.

Custom fields render as compact `key:value` pills in the chip rail (or, for `user-list`, a chip cluster of users + groups). They don't get the rich primitive widgets. They're searchable and sortable like primitives — except `user-list`, which is not yet wired into the search-operator parser (`agreedBy:alice@himarley.com` is on the roadmap).

## Namespaced tags

Any tag with a colon is treated as namespaced — `customer:acme`, `team:security`, `quarter:q3-2026`. Namespacing is a visual convention, not a separate kind of tag: the chip renders the prefix in a muted style and the value in foreground, and the autocomplete groups suggestions by namespace.

Namespaced tags are still plain tags — no schema, no validation. They become useful when a vocabulary settles: `customer:*` for the customers your team works with, `quarter:*` for the quarter a piece of work belongs to. The tag can later be promoted to a Type if the team needs structured fields against it (per-scope curation permissions apply — see "Who can create Types" below).

## Filtering & sorting

Structured fields extend the existing search operators. Examples:

```
tag:recommendation progress:>50          # ⌘K — recommendations more than half done
tag:incident priority:urgent due:<2026-07-01
tag:decision status:decided trafficLight:green
customer:acme tag:recommendation         # namespace and Type combined
```

Sorting by a structured field uses the same syntax: `sort:due-asc`, `sort:progress-desc`. Saved Views can pin these queries.

## Who can create Types

**Tags**: anyone with edit permission on a Droplet can add or remove them. No gate.

**Types**: scoped curation per feat/044. From the `/types` page in the sidebar (open to any authenticated user), curators can:

- Promote an existing tag to a Type, choosing which primitive fields opt in and declaring any custom fields
- Create a new Type from scratch
- Edit the schema of an existing Type (add fields, edit enum values, deprecate fields)
- Demote a Type back to a plain tag, optionally retaining or dropping its field values

Who can do each of those, on each tier:
- **USER scope** — the owner of the row (anyone authenticated, on their own row)
- **SPACE scope** — Space OWNER or EDITOR of the owning Space
- **GLOBAL scope** — LIBRARY_ADMIN (members of `@drop-library-admins`)

Every promotion / demotion / schema edit lands in the audit log with the actor and timestamp. Demoted Types are retained for 30 days, after which orphaned field values are garbage-collected.

## Drop is not Notion

Types are deliberately *curated*, not *self-serve* at every tier. Anyone can mint a USER-scope Type in their own namespace; SPACE Types require Space admin; GLOBAL Types require LIBRARY_ADMIN. The escalation is what keeps shared vocabularies coherent — anyone can experiment locally, but landing a Type in the org-wide namespace takes deliberate sign-off.

The right shape of the registry is small, well-defined, and shared. The wrong shape is "anything anyone needed once."

## Operations

### Add a tag to a Droplet

Any editor of a Droplet can add a tag from the chip editor on the detail page, or via the CLI at deploy time. Tags are normalized (lowercase, trimmed, deduped) on write. Maximum 20 tags per Droplet, 50 chars per tag.

**CLI**

```bash
# Set tags at deploy time
drop deploy my-page.html --slug my-droplet --tag onboarding --tag q3-2026

# Or update existing tags
drop tags add my-droplet onboarding q3-2026
```

**HTTP**

```bash
curl -X PATCH https://drop.himarley.ai/api/apps/my-droplet \
  -H "Authorization: Bearer $DROP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tags": ["onboarding", "q3-2026"]}'
```

### Promote a tag to a Type

Promotion attaches a schema to the tag name. The CLI surfaces the existing usage count as a courtesy, then mints the registry row. Existing Droplets wearing the tag don't change — their tag chip becomes a Type chip (navy-dot prefix) and the field widgets render in empty state until editors set values. Per feat/044, the gate is the *target tier*: USER tier = anyone; SPACE tier = Space admin; GLOBAL tier = LIBRARY_ADMIN.

**CLI**

```bash
drop tags promote recommendation \
  --description "A proposal awaiting decision." \
  --primitive status:enum:draft,in-flight,decided,blocked \
  --primitive progress \
  --primitive priority \
  --primitive due \
  --primitive trafficLight \
  --primitive owner \
  --custom rice:int \
  --custom quarter:string
```

**HTTP**

```bash
curl -X POST https://drop.himarley.ai/api/types \
  -H "Authorization: Bearer $DROP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "recommendation",
    "description": "A proposal awaiting decision.",
    "schema": {
      "primitives": {
        "status": { "values": ["draft", "in-flight", "decided", "blocked"] },
        "progress": true,
        "priority": true,
        "due": true,
        "trafficLight": true,
        "owner": true
      },
      "customFields": [
        { "key": "rice", "type": "int" },
        { "key": "quarter", "type": "string" }
      ]
    },
    "promoteFrom": "recommendation"
  }'
```

### Set structured field values on a Droplet

Once a Droplet wears a Type, its editors can set the structured field values. Click any widget in the header strip or use the CLI. Values are validated against the Type's schema; null clears a field.

**CLI**

```bash
drop set onboarding-redesign \
  --tag recommendation \
  --field status=in-flight \
  --field progress=74 \
  --field due=2026-06-30 \
  --field priority=high

# Inspect
drop get onboarding-redesign --fields
```

**HTTP**

```bash
curl -X POST https://drop.himarley.ai/api/apps/onboarding-redesign/type-fields \
  -H "Authorization: Bearer $DROP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tag": "recommendation",
    "fields": {
      "status": "in-flight",
      "progress": 74,
      "due": "2026-06-30",
      "priority": "high"
    }
  }'
```

### Search by structured field

The `type:` operator filters by Type-by-name — `type:decision`, `type:inform`, `type:recommendation`. Structured-field operators filter and sort by per-Type values. Mix freely with `from:`, `before:`, etc. Operators are exact match; typos fall back to free-text pg_trgm matching.

The command palette (⌘K) autocompletes values for every operator — type `status:` and press Tab to pick from the active Types' enum vocabulary.

**CLI**

```bash
# Recommendations in flight — Jonathan's go-to query
drop search "type:recommendation status:in-flight"

# Recommendations with a yellow traffic light
drop search "type:recommendation light:yellow"

# Open recommendations more than halfway done, high priority
drop search "type:recommendation status:in-flight progress:>50 priority:high"

# Incidents due this week, sorted by due ascending
drop search "type:incident due:thisweek sort:due-asc"

# Everything that's overdue, regardless of Type
drop search "due:overdue sort:due-asc"

# All work for the Acme customer
drop search "customer:acme"

# Combine with a person and a date
drop search "type:decision from:kate before:2026-09-01"
```

**HTTP**

```bash
curl "https://drop.himarley.ai/api/search?q=type:recommendation+status:in-flight" \
  -H "Authorization: Bearer $DROP_TOKEN"
```

### Operator cheatsheet

Every operator the search bar understands. Combine with spaces — they AND together. The palette's bottom panel (`?` to toggle) renders the same list grouped by purpose.

**CLI**

```text
Filter by Type        type:recommendation       type:decision
Match a status        status:in-flight          status:decided
Priority              priority:high             priority:urgent
Progress              progress:>50              progress:<=20
Due date              due:overdue               due:thisweek
                      due:<2026-07-01           due:>2026-06-01
Traffic light         light:yellow              light:red
Sort                  sort:due-asc              sort:progress-desc
Owner (field)         owner:jonathan
Author (Droplet)      from:jonathan             @jonathan
Tag                   tag:design                #design
Namespaced tag        customer:acme             team:eng
Scope to Space        space:engineering         >engineering
Audience              access:private            access:everyone
Shared with me        shared:me
Starred               is:starred
Archived              is:archived
Updated date          before:2026-01-01         after:2026-05-01
```

