Reference
Timeline Format
The Cronolog import/export format. Use this to construct, parse, or migrate timelines programmatically.
Building an external integration? The JSON feed uses this same schema. See the integration API docs for how to embed a Cronolog timeline in your own site.
Overview
Cronolog supports two import/export formats:
ZIP archive .zip
A ZIP file containing a timeline.json manifest plus image files.
This is the full export — metadata, events, and all attached photos.
JSON file .json
A standalone JSON file. Identical schema to timeline.json inside the ZIP,
but image fields are ignored on import.
When importing a ZIP, image files are read from the paths declared in each event's
images[].path field.
When importing a JSON file, all images arrays are ignored.
ZIP archive layout
timeline.zip
├── timeline.json ← manifest (required)
├── images/
│ ├── cover.jpg ← cover image (optional, path declared in manifest)
│ └── events/
│ ├── 0/ ← first event (index matches event order in manifest)
│ │ ├── photo.jpg
│ │ └── photo2.webp
│ ├── 1/
│ │ └── photo.jpg
│ └── …
Image filenames within each directory are taken from the original blob filenames.
The directory index (0, 1, …)
corresponds to the event's position in the events array.
JSON schema
Top-level fields
| Field | Type | Required | Notes |
|---|---|---|---|
| format_version | integer | no | Always 1. Ignored on import. |
| source_timeline_id | integer | no | Internal ID of the originating timeline. Ignored on import. |
| exported_at | string | no | ISO 8601 datetime. Ignored on import. |
| timeline | object | yes | Timeline metadata. See below. |
| taxonomy_terms | array | no | Tag vocabulary for this timeline. See below. Omit or set to [] if unused. |
| events | array | yes | Ordered array of event objects. See below. |
timeline object
| Field | Type | Required | Notes |
|---|---|---|---|
| title | string | yes | Display name of the timeline. |
| description_raw | string | no | Plain text description. Supports *bold*, _italic_, - lists, and [text](url) links. |
| visibility | string | no | "private" (default), "unlisted", or "public". Older exports may use a "publicly_visible" boolean instead; true maps to "unlisted". |
| sort_direction | string | no | Display order: "asc" = oldest first (default), "desc" = newest first. Defaults to "asc" on import. |
| cover_image | string | no | Path to cover image within ZIP (e.g. "images/cover.jpg"). Ignored for JSON-only imports. |
| cover_image_metadata | object | no | Focal point and photo metadata for cover image. See metadata object below. |
taxonomy_terms[] object
Defines the tag vocabulary for this timeline. Terms are referenced by name from each event's
tags array.
Parents are resolved by name, so list ancestor terms before their children, or let the importer sort it out
(it makes multiple passes until all parents are resolved).
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | yes | Term name. Max 50 characters. Case-insensitively unique within the timeline. |
| parent_name | string | no | Name of the parent term. Omit or set to null for root-level terms. |
events[] object
Events are imported in the order they appear in the array. The stored order on the timeline is determined
by start_at after import, not array position.
| Field | Type | Required | Notes |
|---|---|---|---|
| title | string | yes | Event title. |
| description_raw | string | no | Plain text. Same markup support as timeline description. |
| date_input_raw | string | yes* | The human-readable date string as originally entered (e.g. "June 1977", "1985", "late '90s"). Used for display. See date formats below. |
| end_date_input_raw | string | no | End of a date range (e.g. "March 2004"). Omit for point-in-time events. |
| start_at | string | yes* | ISO 8601 datetime. The computed start boundary for this event. Used for chronological ordering. |
| end_at | string | yes* | ISO 8601 datetime. The computed end boundary. For a year event this is Dec 31 23:59:59. |
| precision | string | yes* | "exact" or "bounded" or "symbolic". |
| precision_unit | string | yes* | "day", "month", "year", or "phase". |
| display_label | string | no | Human-readable date label shown on the timeline (e.g. "June 1977" or "1999 - 2005"). For ranged events (end_date_input_raw present), always recomputed from the raw inputs on import — any supplied value is replaced. For point-in-time events, derived from date_input_raw if omitted. |
| tags | array | no | Array of tag name strings (e.g. ["Career", "Travel"]). Each name must match a term in taxonomy_terms[]. Unrecognised names are silently ignored on import. |
| images | array | no | Ordered array of image objects. Ignored for JSON-only imports. |
* Fields marked yes* are required when constructing JSON for import. When generating JSON programmatically,
provide both date_input_raw (human-readable)
and the computed start_at / end_at / precision / precision_unit fields.
images[] object
| Field | Type | Notes |
|---|---|---|
| path | string | Path to the image file within the ZIP archive (e.g. "images/events/0/photo.jpg"). |
| content_type | string | MIME type (e.g. "image/jpeg", "image/webp"). |
| position | integer | 1-based display order within the event. Images are sorted by this value on import. |
| metadata | object | Optional. See metadata object below. |
metadata object
Appears on both timeline.cover_image_metadata
and each images[].metadata. All fields optional.
| Field | Type | Notes |
|---|---|---|
| focal_x | float | Horizontal focal point as a fraction 0.0–1.0 (0 = left edge, 1 = right edge). Used for smart image cropping. |
| focal_y | float | Vertical focal point as a fraction 0.0–1.0 (0 = top edge, 1 = bottom edge). |
| photo_taken_at | string | ISO 8601 datetime when the photo was taken, if known. |
Date formats for date_input_raw
Cronolog's date parser accepts a wide range of human-readable formats. The corresponding
start_at,
end_at,
precision, and
precision_unit
values are derived from the input.
| Input example | precision_unit | display_label |
|---|---|---|
| 1985 | year | 1985 |
| '85 / 85 | year | 1985 |
| June 2003 | month | June 2003 |
| jun 2003 / 2003-06 / 6/2003 | month | June 2003 |
| June 15 2003 | day | June 15, 2003 |
| 2003-06-15 / 6/15/2003 | day | June 15, 2003 |
| 1980s / '80s | phase | The 1980s |
| early 1980s / early '80s | phase | Early 1980s |
| mid 1980s | phase | Mid 1980s |
| late 1980s / late '85 | phase | Late 1985 |
| early oct 2027 | phase | Early October 2027 |
| circa 1920 / ~1920 / ca. 1920 | year | 1920 |
| 1999 - 2005 / 1999 to 2005 | year (start) | 1999 - 2005 |
| June 2003 - March 2004 | month (start) | June 2003 - March 2004 |
| between 1940 and 1945 | year (start) | 1940 - 1945 |
Two-digit years use a rolling pivot: values ≤ the current two-digit year resolve to this century; larger values resolve to the previous century.
Approximation prefixes (circa,
ca.,
~) are stripped before parsing.
Complete example
A minimal but complete timeline.json
with two events — one exact date, one date range — and one image attachment.
{
"format_version": 1,
"source_timeline_id": 42,
"exported_at": "2025-11-03T14:22:00Z",
"timeline": {
"title": "Life of Francis",
"description_raw": "A record of my grandmother's life.",
"visibility": "private",
"sort_direction": "asc",
"cover_image": "images/cover.jpg",
"cover_image_metadata": {
"focal_x": 0.5,
"focal_y": 0.3
}
},
"taxonomy_terms": [
{ "name": "Travel" },
{ "name": "Canada", "parent_name": "Travel" }
],
"events": [
{
"title": "Moved to Vancouver",
"description_raw": "She was 19. \"I only had one suitcase.\"",
"date_input_raw": "1962",
"end_date_input_raw": null,
"start_at": "1962-01-01T00:00:00Z",
"end_at": "1962-12-31T23:59:59Z",
"precision": "bounded",
"precision_unit": "year",
"display_label": "1962",
"tags": ["Travel", "Canada"],
"images": []
},
{
"title": "World tour",
"description_raw": "First time abroad. Three months, twelve countries.",
"date_input_raw": "July 1971",
"end_date_input_raw": "September 1971",
"start_at": "1971-07-01T00:00:00Z",
"end_at": "1971-09-30T23:59:59Z",
"precision": "bounded",
"precision_unit": "month",
"display_label": "July - September 1971",
"tags": ["Travel"],
"images": [
{
"path": "images/events/1/passport.jpg",
"content_type": "image/jpeg",
"position": 1,
"metadata": {
"focal_x": 0.48,
"focal_y": 0.35,
"photo_taken_at": "1971-08-15T00:00:00Z"
}
}
]
}
]
}
Notes for agents and automated tools
-
Always provide both the human-readable
date_input_rawand the computedstart_at/end_at. The import uses the explicit datetime fields directly;date_input_rawis stored for display and future re-parsing. -
For fuzzy dates, derive
start_atandend_atfrom the boundaries implied by the precision: a year-precision date spans Jan 1 00:00:00 to Dec 31 23:59:59; a month-precision date spans the first to last day of the month. -
All datetimes must be ISO 8601 strings in UTC (e.g.
"1962-01-01T00:00:00Z"). -
end_atmust always be greater than or equal tostart_at. -
For JSON-only imports (no ZIP), set
imagesto an empty array or omit it entirely. Image fields in the manifest are silently ignored without a ZIP. -
The
eventsarray can be empty. A timeline with no events is valid. -
Tags are matched case-insensitively during import. Declare all terms in
taxonomy_termsbefore referencing them in eventtags. Terms not present intaxonomy_termsare silently ignored when tagging events. -
Description fields support a small set of inline markup:
*bold*,_italic_,- list item(line-starting hyphen), and[link text](https://url)for hyperlinks. Plain URLs are auto-linked. All other HTML is stripped on render.