Settings → Download gives you a zip: a folder of plain files holding everything LoopUpward knows about you. It is built in your browser and goes nowhere else.
This page documents every file and every column. The same reference ships
inside the zip as README.md, so the export explains itself even if you never
come back here.
LoopUpward contains no AI and never calls one. What it does instead is record honestly — including the parts that did not go well — so that when you want to ask something of your own life, you have the material to ask with. The export is the deliverable; the rest of the app exists to make it worth reading.
Every file has a day (or planned_day) column holding a local date,
YYYY-MM-DD. It is not always the calendar day its timestamp falls on.
Routines whose window wraps past midnight belong to the evening they
started. A night routine set to 21:00 → 02:00 is one thing that happens on one
night, so ticking its last step at 1:15am on the 9th writes day = 2026-08-08.
The spill ends when that routine's own window ends.
Nothing else moves a day; every other row uses its calendar day.
Group by day for "how many days did I do this". Group by the timestamp for
"what time of day do I do this". Both are in every file on purpose.
Each moment appears three ways, so you never do timezone arithmetic:
| Column | Example | What it is |
|---|---|---|
*_at |
2026-08-18T06:12:44+05:30 |
ISO-8601 with the UTC offset in force at that moment |
*_local_time |
06:12 |
the wall clock you saw |
*_weekday |
Tuesday |
the weekday it fell on |
Durations are always minutes, one decimal, everywhere.
Empty means "not recorded", never zero. A blank mood is a day you did not
rate. Nothing in the bundle is backfilled with a guess.
focus_sessions.csv and events.ndjson recorded the UTC offset in force at the
moment, so they are exact — travel and daylight saving included. Every other
file is rendered in your own timezone from Settings, which is right except
during a trip. manifest.json lists which files got which, under timezone.
The behavioural event log and the focus-session log started later than the rest
of the app. manifest.json gives the exact date under observation_begins, and
the bundle's own README repeats it.
Before that date:
- Zeros in
daily.csvforapp_opens,eventsandfocus_sessionsmean not observed, not "did nothing". - What a habit's target was, what schedule it was on, and which steps a routine
held cannot be reconstructed — so the files that need those say
unknownin a*_sourcecolumn and leave the verdict blank.
A blank verdict is deliberate. Raising a water habit from two glasses to three must not turn every past two-glass day into a failure, and a routine step added last week must not appear as six months of skipping. Where the export cannot establish what was being asked for at the time, it says so instead of using today's answer.
| File | What it is |
|---|---|
README.md |
this reference, written for someone who has never seen the app |
manifest.json |
coverage windows, timezone grades, and the vocabulary of every coded column — what the record can and cannot know |
context.md |
your timezone, when your day rolls over, and what each area of your life is for |
summary.md |
the computed numbers, formatted to read |
The download can be narrowed to the current week, the current month, or any range you pick. A scoped bundle has the same files and the same columns as a whole-life one, so two of them can be compared or concatenated. Two things are added so a slice reads on its own:
items.csv,areas.csvandlabels.csvalso carry rows from outside the window whenever something inside it points at them, markedin_window = false. No id in the bundle is left dangling.items.csvcarriesopening_value, what each tracker read the moment the window opened, without which a counter that moved 3 to 7 inside the window cannot be told from one that started at zero.
manifest.json records the window under window.
- Record — what happened, at the time it happened:
logs.csv,actions.csv,focus_sessions.csv,events.ndjson,habit_days.csv,routine_steps.csv,seeds.csv,list_entries.csv,day_order.csv, and the journal / reflection / note files. - State as of export — how things look today:
items.csv,areas.csv,labels.csv,context.md. A goal reading "done" here says nothing about the journey; that is in the Record files. - Derived — plain counting over the Record, and recomputable:
schedule_expectations.csv,daily.csv,summary.md.
Everything else records what you did, and a missed day is not a row anywhere —
it is the absence of one. This file supplies the other half: one row per
scheduled item per day it was alive, saying whether that day was required
(a fixed schedule named it), not_due, eligible (a quota schedule like
"4× a week", where the period is owed rather than the day), or unknown.
Adherence becomes a join rather than a reconstruction: filter
expectation = required and take the share with met = true. Retiring a habit
ends its expectations that day, so the months after you deliberately stopped are
not counted against you.
area_id · name · emoji · color · position · description ·
why_it_matters · target_share (0–1) · created_at / created_local_time /
created_weekday.
Every node, including finished, archived and trashed ones.
| Column | Meaning |
|---|---|
item_id, title |
identity |
kind |
note, folder, quote, idea, dream, goal, habit, routine, list, project, book, milestone, principle, promise, lesson, memory |
tracker |
none, check, counter, percent, money, habit, book |
status |
active, done, someday, archived |
area_id, area_name |
resolved up the parent chain, exactly as the app reads it |
parent_id, parent_title |
what it nests inside |
horizon |
someday, life, year, quarter, month, week, today, date |
horizon_period |
which instance: 2026-W28, 2026-08, 2026-Q3, 2026, or the pinned day |
date_repeats_yearly |
a birthday rather than a one-off |
target, current, unit, progress_fraction |
the meter |
cadence, cadence_days, cadence_count |
the schedule. Weekday numbers are 0 = Sunday |
window_start, window_end |
routines only. End before start = wraps past midnight |
pulled_today, pinned, labels, note |
the rest of its state |
step_count, planned_minutes, entry_count |
routine and list contents |
created_*, completed_*, deleted_* |
three timestamp groups |
age_days, days_to_complete |
computed for you |
Every concrete thing planned for a day. planned_day is what it was meant
for; done_at is when it was actually ticked; days_late is the gap, and
negative means early.
Moves between days are not visible here — this file only has the final date.
Every move is an action.rescheduled event, and the totals are in
summary.md.
amount is how much finishing it moves the linked item's counter. 0 is a
deliberate choice: a directional step that belongs to a goal without pretending
to move its meter.
Every unit of progress ever recorded. Item counters are derived from these, not the other way round.
op is add (a delta — a habit day, +1 chapter) or set (a snapshot — money
now reads ₹40,000). Sum add rows; take the last set row. Mixing them
gives nonsense.
source says how the value got there:
| Value | Means |
|---|---|
today_check |
ticked on the Today list |
item_page |
changed on the item's own page |
focus_timer |
recorded inside a focus session |
routine_run |
written by a routine's script |
parent_cascade |
a child finished, so this parent's meter moved. via names the child |
manual |
typed in somewhere else |
import |
carried in from another device |
unknown |
written before provenance existed — not a bug, and not a claim it was manual |
One row per timer attempt, written when it ended for any reason. Abandoned attempts sit here on the same footing as completed ones; that is the point of the file.
kind is focus, routine_step, routine_run, day_run or rest.
outcome is completed, expired (countdown ran out and the screen was
left), abandoned (closed mid-session), skipped or interrupted (swapped
for something else).
planned_minutes blank means it was untimed and counted up.
actual_minutes is wall clock with pauses excluded.
over_under_ratio is actual ÷ planned — above 1 means it took longer than
you thought.
A routine produces one routine_run row plus one routine_step row per
step. Do not add both, or you double-count the time.
What a habit or routine meant on one specific day. "Clean" is the habit;
"clean the side desk" is what it meant on Tuesday. Carries day_plan,
whether the day was logged, value_logged against daily_target (2 of 3
glasses), and steps_done / steps_total for routines.
One row per routine, per day, per step. position is where the step sits in
the written script; tick_order is the order you actually did them, which is
often different. done_at is blank on days recorded before step timestamps
existed, even where done is true.
One row per day, everything paired and nothing interpreted: journal presence, mood, energy, sleep hours and quality, stress, focus rating, tasks planned and completed, habit and routine days, focus minutes planned versus actual, abandoned sessions, pauses, app opens, first open, last write, distinct routes, and total events.
The app deliberately does not compute correlations for you. It emits honest columns; the question is yours.
There is no usage table: the app-open columns are derived from app.opened and
page.viewed events, so there is one record of what happened rather than a
counter to reconcile.
list_entries.csv— lines inside list items.picked_atis when a line became "the one I'm on";days_being_triedcounts from there.seeds.csv— raw captures.days_restingis how long a thought sat before it grew into something.labels.csv— your tags, withitems_taggedcounted for you.day_order.csv— the manual order you dragged a day into, one row per position.entry_kindseparates a realactionfrom a scheduledhabitrow or a longer-horizonitempulled onto today.
The full behavioural log: one JSON object per line, oldest first. Read it with
jq, or pandas.read_json(lines=True).
Every line carries id, type, at, at_ms, local_time, weekday, day,
timezone, utc_offset_minutes, item_id, item_title and a payload.
Notable types:
type |
What it records |
|---|---|
item.created |
the birth snapshot: kind, tracker, horizon, area, parent, target, cadence, labels |
item.horizon_changed |
re-scoping — how a goal drifts from "this quarter" to "someday" |
item.completed |
with ageDays and openActionsRemaining |
action.rescheduled |
the procrastination signal — from, to, daysMoved |
routine.step_skipped |
sentToBack: true = "skip for now"; false = an optional step left out |
day_run.abandoned |
with completedBefore — how far it got |
day.reordered |
via: "drag" or via: "sort" — different acts, recorded differently |
app.opened, page.viewed |
being here at all. Routes are recorded in shape (/item/[id]), never which note you had open |
Events are append-only. Nothing is edited or deleted, including the records of things that did not work out.
journal.md— every daily entry in order, with ratings and your words.reflections.md— every period reflection, and whether its intentions were met. Scoring is plain counting against the named items: days logged for a habit, units added for a meter, done-or-not for a one-off. An intention naming nothing countable is shown as not scored.notes.md— note bodies, in the app's own markdown.
Every table exactly as stored, plus both streams and your settings. If a CSV lost some nuance to being a table, it is here.
- What time of day do I really work, versus when I think I do?
- How much longer do things take than I plan for?
- What do I keep pushing, and by how many days in total?
- Which routine step do I quit on?
- Am I creating things faster than I finish them?
- Which weekday breaks my streaks?
- Does sleep track with what I get done?
The bundle is identical either way. Signed in, the rows come from your private cloud; signed out, they come from this browser's own storage. The same generator builds the same files, so keeping your data local never costs you a lesser export.