Developers · How it works

Records

What your fetcher prints, one JSON object a line: values, events and items. Their keys and dates, every rule a run is held to, and how each change is kept.

A record is one JSON object on a line of your fetcher's output, in a series your manifest declares, of that series' kind. The examples are Harbour Almanac's, checked against its manifest on every build.

Kind
What it is
Its key
Kind
value
What it is
Numbers on a date
Its key
series and date, and period when dated by publication
Kind
event
What it is
Something scheduled, that happened, or that was cancelled
Its key
series, date and id
Kind
item
What it is
A dated title with an https link
Its key
series, date and id

Values

jsonl
{"kind":"value","series":"tide","date":"2026-10-01","fields":{"high":2.2,"low":0.63}} {"kind":"value","series":"catch","date":"2026-08-10","period":"2026-07","fields":{"landed":466}} {"kind":"value","series":"catch","date":"2026-09-10","period":"2026-07","fields":{"landed":461.6},"revises":"2026-08-10"}

fields holds 1 to 16 finite numbers, each a field the series declares; one left out is not drawn. An observation date is the day described: when the source changes that day's number, print the new one under the same key. A publication date is the day published, period says what it covers (2026-07, 2026-Q3), and a correction is a new record on its own day with revises naming the date it corrects: July's first figure stays on 10 August, the day it was known. Use observation for rates and readings, publication for statistics revised in later releases. period and revises on an observation refuse the batch.

Events and items

jsonl
{"kind":"event","series":"council","id":"meeting-2026-07-1","date":"2026-07-02","state":"happened","title":"Harbour council: berth fees","value":11.5} {"kind":"event","series":"council","id":"meeting-2026-10-3","date":"2026-10-15","state":"scheduled","title":"Harbour council meeting"} {"kind":"item","series":"notices","date":"2026-10-01","id":"nm-2026-10-01-1","title":"Tide table for October 2026 published","url":"https://notices.harbour-almanac.example/2026/10/01/1"}
  • •
    An event's state is scheduled, happened or cancelled; when it takes place, print it again with the same id and date as happened. Its value, optional, is a number it decided, in the series' unit.
  • •
    id is 1 to 64 characters without spaces, the same every run: the source's own, or made from something that stays when the event moves.
  • •
    An event's id on a new date means it moved, and the old date reads Moved to Thu 22 Oct. Print each id once a run. An item's id on another date is another record.
  • •
    A title is plain text, at most 200 characters, never markup; an item needs one. An item's url is https, to a source's host or one links.hosts declares. Anywhere else refuses the batch:
jsonl
{"kind":"item","series":"notices","date":"2026-10-01","id":"nm-2026-10-01-1","title":"Tide table for October 2026 published","url":"https://example.com/tides"}

The rules every batch is held to

Nothing is after today in your zone, except a scheduled event (predictions). Every date is a real YYYY-MM-DD. Each kind has its own keys, and any other is refused, so a misspelt feilds never passes quietly. A run prints at most 250,000 records. The Server reads the whole batch against the approved manifest before writing any: one bad line refuses every line, and the run fails with its reason. Here two lines share a key:

jsonl
{"kind":"value","series":"tide","date":"2026-10-01","fields":{"high":2.2,"low":0.63}} {"kind":"value","series":"tide","date":"2026-10-01","fields":{"high":2.21,"low":0.63}}

Revisions and withdrawal

Your fetcher needs no memory: print what the source shows now. In the transaction that writes the run, the Server compares it with what it stored, and nothing a reader once saw is overwritten without a trace.

The run prints
It becomes
The run prints
A new key
It becomes
A new record
The run prints
A stored key, same content
It becomes
Nothing
The run prints
A stored key, different content
It becomes
A revision: the new content, the old kept beside it
The run prints
A stored event id on another date
It becomes
The old date marked moved to the new one
The run prints
A withdrawn key
It becomes
The record back, as a revision

Content is a value's fields and revises, an event's state, value and title, an item's title and link. Each record keeps the run, version and digest that last wrote it.

Snapshot windows

A source also changes its mind by leaving things out. Kanboombo notices only inside a series' snapshot, where your run is the whole truth: { "back": "30d", "ahead": "0d" } is every record from 30 days ago to today. Inside it, a stored record a successful run does not print counts one miss (failed runs count nothing); printed again, its count starts over; on the third miss in a row it is withdrawn, hidden with its history kept, and it comes back as a revision if shown again. A moved event is not counted on its old date. Outside a window, or with none, nothing is counted missing: Harbour Almanac's notices have none on purpose. Keep the window inside what your source shows (why).

Many at once wait for a person

A damaged file can look like a source that withdrew a great deal. When one run would withdraw more than 10 records of a series, or more than 1% of a window holding 100 or more, none are withdrawn: the rest is written, the plug-in shows Held with the first missing dates, and a person chooses Accept removals or Keep them (their count starts over). Until then they stay on readers' days.

Harbour Almanac, a week later

fixtures/runs/later/, 8 October, corrects a provisional tide, records a meeting's outcome and moves another, three revisions:

jsonl
{"kind":"value","series":"tide","date":"2026-09-28","fields":{"high":2.46,"low":0.35}} {"kind":"event","series":"council","id":"meeting-2026-10-1","date":"2026-10-01","state":"happened","title":"Harbour council: berth fees","value":11.75} {"kind":"event","series":"council","id":"meeting-2026-10-3","date":"2026-10-22","state":"scheduled","title":"Harbour council meeting"}

A faulty reading gone from inside the window counts one miss. Records stay when a reader turns a plug-in off or a person removes it; only a purge deletes them.

Every field

PluginRecord

One line your fetcher prints: a value, an event or an item.

One of: PluginValueRecord, PluginEventRecord, PluginItemRecord.

PluginValueRecord

Numbers on a date.

Field
Type
Required
Description
Field
kind
Type
"value"
Required
yes
Description
value
Field
series
Required
yes
Description
A value series the manifest declares.
Field
date
Required
yes
Description
The date it describes (observation) or was published (publication). Never after today.
Field
period
Type
string; at most 32 characters
Required
Description
For publication dates: what it describes, such as 2026-08 or 2026-Q3. Empty for observations.
Field
fields
Type
object of number
Required
yes
Description
The series' fields and their numbers. Finite numbers only.
Field
revises
Required
Description
For publication dates: the date of the earlier record this one corrects.

PluginEventRecord

Something scheduled, that happened, or that was cancelled.

Field
Type
Required
Description
Field
kind
Type
"event"
Required
yes
Description
event
Field
series
Required
yes
Description
An event series the manifest declares.
Field
id
Type
string; 1 to 64 characters
Required
yes
Description
Your id for the event, stable across runs: the same id on a new date moves it. 1 to 64 characters, no spaces.
Field
date
Required
yes
Description
The day it happens. Only a scheduled event may be after today.
Field
state
Required
yes
Description
scheduled, happened or cancelled.
Field
value
Type
number
Required
Description
A number it decided, such as a rate. Optional.
Field
title
Type
string; at most 200 characters
Required
Description
Plain text, 200 characters or fewer. Never markup.

PluginItemRecord

A dated link with a title, such as a press release.

Field
Type
Required
Description
Field
kind
Type
"item"
Required
yes
Description
item
Field
series
Required
yes
Description
An item series the manifest declares.
Field
date
Required
yes
Description
The day it was published. Never after today.
Field
id
Type
string; 1 to 64 characters
Required
yes
Description
Your id for it, stable across runs. 1 to 64 characters, no spaces.
Field
title
Type
string; 1 to 200 characters
Required
yes
Description
Plain text, 1 to 200 characters.
Field
url
Type
string; at most 1,000 characters
Required
yes
Description
An https link to a source's host or one links.hosts declares.

PluginEventState

scheduled may be dated in the future; happened and cancelled may not.

  • •
    Type: "scheduled", "happened", "cancelled"

PluginDate

A calendar date, YYYY-MM-DD. It is never converted between time zones.

  • •
    Type: string
  • •
    Pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$
  • •
    Example: 2026-09-28