Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.myme.so/llms.txt

Use this file to discover all available pages before exploring further.

Types are schemas. Every item has a type, and its properties are validated against that type’s schema on write.

Custom types are the default path

core.* is small and narrow on purpose. Most apps register their own types — inheritance from core.* is optional, not expected. A type earns core-set inclusion only by passing four tests:
  1. Life-noun. Names something a person would describe as a thing in their life, not infrastructure or app state.
  2. Stable shape. Canonical fields don’t change with context.
  3. Cross-app meaning. Multiple unrelated apps want to read each other’s writes in the raw form, without translation.
  4. Stewardship. The core maintainers are willing and able to own the schema long-term.
Most candidates fail. That’s the feature — the core is the lingua franca precisely because it’s disciplined. The same rule applies recursively to subtypes (see Why core file subtypes stop where they do). Tool-specific things are not core. Memories, summaries, embeddings, AI-specific artefacts — none have broad shape consensus across producers, so they become custom types: claude.memory, chatgpt.memory, cursor.memory. Different shapes are honest.

Namespacing

Five tiers. Reserved roots tell a reader (or parser) the shape at first glance.
NamespacePurpose
core.*Platform-shipped, immutable, narrow.
system.*Platform-internal operational types. See System types.
app.<app-name>.<type>App-published types. The app. prefix separates app trust from publisher trust.
user.<type>Unpublished personal types, space-scoped.
<publisher>.<type>Community-published, installable.
Type identifiers use dots. No forward slashes: demo.web_gallery, not demo/web-gallery. The reserved roots — core, system, app, user, marfa — cannot be claimed as publisher handles. Any first segment that isn’t a reserved root is interpreted as a publisher handle. User types are flat by convention but can extend via inheritance where it earns its place. Sibling is the default; inherit only when every ancestor field is semantically required for your type. See Sibling vs child below and the Namespace concept page for the full grammar.

Why core file subtypes stop where they do

core.file has three subtypes: image, audio, video. There is no core.file.document. core.file.image carries width and height; image viewers rely on those. core.file.audio and core.file.video carry duration (plus dimensions for video). core.file.document doesn’t earn a subtype — PDFs, Word docs, Markdown, plain text, EPUBs don’t share fields beyond what core.file already carries (blob_ref, mime_type); a generic file viewer reads them all the same way. For the full rubric behind the boundary, see Authoring types — when a subtype earns its place.

The core catalog

22 types across four families. Required fields only — full schemas in the API reference.
TypePurposeRequired
core.noteUser-authored textbody
core.bookmarkSaved external content
core.taskSomething to be donetitle
core.eventSomething that happens at a timetitle
core.highlightUser engagement with contenttext
core.messageCross-platform messagebody, from
core.highlight carries its highlighted parent via a references edge, not an embedded reference. core.message is a light cross-platform handle — threading reuses the existing in-thread and parent-of edges; tool-specific richness (reactions, read receipts, channel ids) lives in app namespaces, not core. core.note and the optional bodies on core.bookmark and core.task are markdown.
TypePurposeRequired
core.entityThings with identityname
core.entity.personPeoplename (inherited)
core.entity.placeLocationsname (inherited)
core.entity.person adds given_name, family_name, pronouns, organization, job title, birthday. core.entity.place adds address components plus latitude / longitude / altitude / timezone.
TypePurposeRequired
core.mediaContent the user engages withtitle
core.media.bookBookstitle (inherited)
core.media.articleArticlestitle (inherited)
core.media.filmFilmstitle (inherited)
core.media.songSongstitle (inherited)
core.media.albumAlbumstitle (inherited)
core.media.podcastPodcast episodestitle (inherited)
core.media.seriesTV seriestitle (inherited)
core.media.tv_episodeTV episodestitle (inherited)
All subtypes inherit the shared media fields (author, url, publisher, published_at, image_url, language, body, notes) and add their own (isbn, duration, section, track_number, etc.).
TypePurposeRequired
core.fileBinary contentblob_ref, mime_type
core.file.imageImagesblob_ref, mime_type, width, height
core.file.audioAudioblob_ref, mime_type, duration
core.file.videoVideoblob_ref, mime_type, width, height, duration
blob_ref is a SHA-256 content hash — see Items for the blob upload flow.
core.message is intentionally light — body + from + optional to. Tool-specific richness (reactions, read receipts, channel ids, attachment vocabularies) varies too much per producer; apps and publishers register their own message types in their own namespaces (e.g. slack.message, apple.imessage) when they need it.There is no core.collection — collection-like groupings are custom types plus parent-of edges. See Edges.There is no core.thread — threads are implicit, defined by in-thread edges. See Edges.

Custom types

Register a custom type at runtime:
POST /types
{
  "id": "readwise.reader_document",
  "version": 1,
  "parent": "core.bookmark",
  "fields": {
    "location": { "type": "enum", "enum_values": ["inbox", "later", "shortlist", "archive", "feed"] },
    "word_count": { "type": "integer" },
    "reading_time": { "type": "integer" },
    "html_content": { "type": "string" },
    "parsed_at": { "type": "datetime" }
  }
}
Tenant-admin for tenant types; platform-only for core.* and system.*. Core types can’t be modified or deleted via the API.

Sibling vs child

A custom type can be a sibling of a core type (its own shape, optionally declaring compatible-with: core.<type>) or a child that inherits. Both are first-class. Visual schema builders default to sibling — AI-assisted authoring tends toward over-inheritance, and a bad inheritance choice is hard to recover from (children cannot redefine ancestor fields). Sibling is recoverable; you can always add inheritance later. The rubric: inherit only if every ancestor field is semantically required for your type. Otherwise sibling, optionally compatible-with. compatible-with is a server-checkable declaration: the platform verifies at registration that your type is a structural superset of the named type, optionally under name mappings. It is not a free-text tag — claims that don’t hold are rejected. Inheritance is single-parent and additive-only. Children may add fields; they cannot redefine or reshape ancestor fields. Cross-discoverability differs between the two. A child appears in parent-type queries automatically — ?type=core.bookmark returns inheriting subtypes. A sibling with compatible-with does not; consumers must know the type identifier to query for it. Prefer inheritance when cross-readability by generic clients matters; stay sibling when the shape genuinely diverges. See Authoring types — cross-discoverability for the deeper trade-off.

Inheritance

A custom type can inherit from a core type.

Reads inherit down

GET /items?type=core.bookmark returns both core.bookmark items and readwise.reader.document items. Each carries its actual type; inclusion is by inheritance. A generic reader querying core.bookmark can rely on every result having the core.bookmark contract — title, body, url mean the same thing across children.

Writes require the exact type

A credential with write core.bookmark can’t update a readwise.reader.document — writes require the specific type in scope. This prevents cross-app write-through corruption while preserving the read-side interop benefits.

The inheritance rule

Inherited fields keep their parent-type meaning in every descendant. A child type may not redefine what a parent field means. If a child needs different content, it adds a new property alongside — it doesn’t repurpose the parent’s. Example: core.bookmark.body is the user’s annotation on the bookmark. A Reader-style child type that wants to carry the parsed article text adds parsed_body (or similar) — it doesn’t redefine body to mean the article content. Attempting to redefine a parent field at registration returns 400 inheritance_violation.

Schema evolution across inheritance

Items carry schema_version. Incrementing a parent type’s schema doesn’t automatically re-validate inheriting-type items. Inheriting types declare which parent versions they’re compatible with and migrate when ready.

Registration

  • Runtime registration. POST /types with a JSON schema. Admin only within the space; reserved-root namespaces (core.*, system.*) reject non-platform credentials. Types become available in the space.
  • Schema evolution. Types carry a version. Registering an updated type computes a structural diff against the prior version; the version bump must match the diff class (additive → minor; field removed or required-tightened → major; descriptive-only → patch). Mismatched bumps are rejected at registration. See Authoring types.
  • Global publication. Community types are published under a publisher handle to the Marfa type registry; published types are referenceable by any space that opts in. See Handles and publishers.

Field types

Type field definitions support:
  • string, integer, number, boolean, array
  • url, email, date, datetime (string types with semantic validation)
  • enum (with enum_values)
  • Nested objects via fields
Required fields are declared with required: true on the field definition. Missing required fields return 400 validation_error.

Type-specific time fields

Every item carries timestamp — the primary user-meaningful time for the item (a photo’s captured-at, a note’s authored-at). Type-specific time properties appear only when a type genuinely carries more than one meaningful time.
  • core.task adds due_at, completed_at, optional starts_at — three distinct moments, none reducible to timestamp.
  • core.event adds starts_at and ends_at.
  • core.media.* adds published_at — the content’s publication date, distinct from when the user engaged with it.
When a type has one meaningful user-time, that time is timestamp and the type adds no extra field. A highlight’s “when-highlighted” is timestamp; there is no separate highlighted_at.

Display hints

Types may declare an optional display_hints block:
"display_hints": {
  "title_field": "title",
  "body_field": "body"
}
This points generic readers (catalog views, search UIs, item previews) at the canonical title and body fields. Hints are inherited from the nearest ancestor when a subtype omits them.

Merge policy

Types may declare an optional merge_policy block that controls how concurrent edits resolve per field. Long-text fields like body and notes typically keep both copies; scalars and metadata typically take the last writer’s value.
"merge_policy": {
  "fields": {
    "body": "keep_both_copies",
    "notes": "keep_both_copies"
  },
  "default": "last_writer_wins"
}
The two strategies are last_writer_wins and keep_both_copies. Field names in merge_policy.fields must exist in the type’s fields map. Inheritance behaves like display_hints and version_policy — a subtype’s policy merges field-by-field over its parent’s, and a subtype that omits the block inherits the parent’s wholesale. See Conflicts for the resolution flow and the per-core-type defaults.