Skip to content
OWLF
Esc
↑↓navigate↵open⌘Jpreview
On this page

Specification

The normative text of OWLF 0.1. Generated from spec/SPEC.md.

OWL is an open JSON format for mood and wellbeing data. One file holds one person’s entries from one app: mood, sleep, emotions, tags, people, notes and journal prompts.

The key words MUST, SHOULD and MAY follow RFC 2119.

Design rules

  1. Keep source values. A document stores what the app stored: its own scale, labels and emotion names. Mapping to another app’s vocabulary is the consumer’s job. So the same file can feed any target, and every mapping can be explained to the person.
  2. Local day first. An entry belongs to the calendar day the person saw in the app. Producers MUST NOT derive that day from a UTC timestamp.
  3. Count what is lost. A field the format cannot carry is counted in dropped, never silently skipped.
  4. No app is special. Nothing in the format belongs to one app. App-specific meaning goes into scale labels, valence or dropped.

Document

Field Type Rule
owlf "0.1" MUST. Spec version.
$schema string MAY. Link to the JSON Schema.
source.app kebab-case string MUST. Adapter id: daylio, how-we-feel.
source.name string MUST. App name as the store lists it.
source.generator string SHOULD. Package and version that wrote the file.
scales.mood Scale or null null when the app has no mood rating (plain journals).
scales.sleep Scale or null null when the app has no sleep rating.
entries Entry[] MUST. Source order or by time.
warnings string[] MUST. File-level notes for people, e.g. “2 files ignored”.

Unknown fields are invalid. New fields arrive with a new spec version.

Fields marked MAY or SHOULD are optional; every other field is required. Empty values are written out: null, [], {} or { "photos": 0 }.

Scale

A scale describes the app’s own rating steps, worst to best.

Field Type Rule
size integer ≥ 1 Number of positions: 5 for a 1–5 scale.
levels Level[] At least one.
continuous { min, max, unit? } MAY. Only for sliders. Levels are then equal bands of min..max.

Level

Field Type Rule
id string MUST be unique in its scale.
label string As the app shows it: “rad”, “7”, “Very good”.
position integer, 0..size-1 0 = worst. Several levels MAY share a position (custom moods in one group).
color #rrggbb MAY. The app’s color for this level.
valence number, -1..1 MAY. The adapter’s reading of the level’s meaning. Set it only when position misleads, e.g. 7 stored values shown as 5 faces.

Consumers map by valence when present, else by position / (size - 1). A scale of size 1 maps to the middle.

Entry

Field Type Rule
id string MAY. The app’s id, for finding duplicates.
date YYYY-MM-DD MUST. Local calendar day.
time HH:mm or HH:mm:ss or null Local wall-clock time. null = unknown.
instant RFC 3339 date-time with seconds and offset (2026-03-01T21:40:00+01:00), or null Only when the file stores an absolute moment. Wins over date + time for ordering.
zoneless boolean MAY. instant comes from a bare epoch; date and time are a guess in the converting device’s zone. Consumers SHOULD re-derive the day in the person’s zone. Requires instant.
utcDate YYYY-MM-DD MAY. The file prints a UTC day next to a local time. Consumers SHOULD re-derive the day in the person’s zone.
mood ScaleValue or null level MUST exist in scales.mood.
sleep ScaleValue or null level MUST exist in scales.sleep.
title string or null
note string or null Free text, line breaks kept.
prompts { prompt, answer }[] Guided questions with answers, source order.
tags { name, group }[] group = the app’s category, null = ungrouped.
emotions { name }[] Names as the app spells them.
people string[] Names as the app spells them. A person on an entry means the person connected with them that day, not that they were together.
media { photos } Counts only. Files are not carried.
dropped { [field]: count } Source fields with no place in this format, with count on this entry.

ScaleValue: { level, raw, label? }. raw is the value exactly as in the file. label MAY be set, only when the file’s label differs from the level’s.

Adapters

An adapter reads one app’s export and writes a document. Contract: Adapter.

  • Package: @owlf/adapter-<id>.
  • detect(files) returns 0 to 1 and never throws.
  • parse(files) returns a valid document or throws AdapterError with message, why and fix.
  • modules lists the kinds of data the adapter reads, in this order. An entry has a module when:
    • mood: mood is set.
    • sleep: sleep is set.
    • emotions: emotions is not empty.
    • tags: tags is not empty.
    • people: people is not empty.
    • notes: title or note has text.
    • prompts: prompts is not empty.
    • photos: media.photos is above 0.
  • Conformance: the adapter ships a synthetic fixture and passes checkAdapter from @owlf/core/testkit. The fixture MUST cover every mood position when the app has mood. The fixture MUST cover exactly the declared modules: each in at least one entry, no other. Real exports never enter the repository.

Versioning

owlf is MAJOR.MINOR.

  • Before 1.0, any version change may break. Consumers MUST reject a version they do not know.
  • From 1.0, a minor version only adds optional fields. Consumers MUST reject an unknown major version and SHOULD read a higher minor version of a known major, ignoring fields they do not know.

Was this page helpful?