Developers · The folder

The manifest, plugin.json

plugin.json, decided in order, then every field from Kanboombo's own schema, every limit, and what changed in contract 1. Unknown keys are refused.

plugin.json is the first thing a reviewer reads and the only thing the Server trusts: your fetcher cannot reach a host you did not declare, nor write a series you did not name. Add "$schema": "https://kanboombo.com/developers/plugins/plugin.schema.json" and your editor completes and checks it as you type. Unknown keys are refused at every level, except $schema: a misspelt fieldnames fails with PLG-MANIFEST-UNKNOWN-KEY instead of being quietly dropped.

Decide it in this order

Who it is. id is 2 to 48 lowercase letters, digits and hyphens, starting with a letter: your folder's name, part of lasting addresses (kanboombo://plugins/<id>), never reused, so make it plain and lasting. name is per language; version is major.minor.patch.

When it runs. timezone, a named IANA zone, never an offset (Dates); schedule, a five-field cron in it, at most 24 runs a day; backfill, how far back the first run fills: all, or a span such as 90d.

Who may read it. audience is public, private or local: Audiences.

What it downloads. At most 16 sources, each a lowercase name used once and an https url with a lowercase host. Set maxBytes, at most 20 MB, close to what you see: a far larger file is more likely an error page than news.

What it writes. 1 to 64 series, each one kind of record:

Decide
Choose
Decide
kind
Choose
value, numbers about a date; event, something that happens on a date, maybe scheduled ahead (a rate decision, with the new rate as its value); item, a dated document to open
Decide
dates
Choose
observation, the day described, where a changed number is a revision; publication, the day published, with period and revises (most monthly statistics)
Decide
Fields
Choose
fields, 1 to 16 in the order shown; fieldNames per language; unit, at most 16 characters (% shows changes in basis points); change, the field whose change readers see, never coloured good or bad
Decide
snapshot
Choose
A window, { "back": "30d", "ahead": "0d" }, inside which every run prints the whole truth: for a source that corrects or removes what it published, not for notices that stand; kept inside what the source shows

cadence tells readers what to expect and schedules nothing. links.hosts lists hosts items may link to besides your sources'; limits may lower seconds and megabytes, never raise them. licence says whose data it is: Data rights.

Mistakes the check catches

check reports the first problem of each part, with its code, so most are fixed in one pass. An offset is not a time zone:

json
{ "contract": 1, "id": "my-plugin", "name": { "en": "Mine" }, "version": "1.0.0", "timezone": "-04:00", "audience": "private", "schedule": "0 7 * * *", "backfill": "all", "sources": [{ "name": "data", "url": "https://example.com/data.csv" }], "series": [{ "key": "main", "kind": "value", "cadence": "daily", "dates": "observation", "name": { "en": "Main" }, "fields": ["value"] }], "licence": { "holder": "Someone", "terms": null, "retain": "unknown" } }

An http source fails with PLG-MANIFEST-SOURCE-URL; an id with capitals or an underscore with PLG-MANIFEST-ID.

Every field

PluginManifest

plugin.json: what your plug-in downloads, what it writes, when it runs and who may read it. Unknown keys are refused.

Field
Type
Required
Description
Field
$schema
Type
string; at most 500 characters
Required
Description
Your editor's schema for plugin.json, such as https://kanboombo.com/developers/plugins/plugin.schema.json. Ignored by Kanboombo.
Field
contract
Type
1
Required
yes
Description
The contract this plug-in follows: 1.
Field
id
Required
yes
Description
The plug-in's id, the same as its folder's name.
Field
name
Required
yes
Description
Its name per language, 80 characters or fewer each.
Field
version
Type
string
Required
yes
Description
major.minor.patch. Raise it with every change you publish.
Field
timezone
Type
string; 1 to 100 characters
Required
yes
Description
A named IANA zone. It decides today, how schedule is read, and how source URLs are filled.
Field
audience
Required
yes
Description
Who may read it: public, private or local.
Field
schedule
Type
string; at most 100 characters
Required
yes
Description
Five-field cron, read in the manifest's time zone.
Field
backfill
Type
"all" or PluginWindow
Required
yes
Description
How far back the first run fills: all, or a span such as 5y.
Field
sources
Type
array of PluginSource
Required
yes
Description
The files Kanboombo downloads for each run. At most 16.
Field
series
Type
array of PluginSeries
Required
yes
Description
What it writes. 1 to 64.
Field
links
Required
Description
Where an item's link may point besides your sources' own hosts.
Field
limits
Required
Description
Lower time and memory limits, if you want them.
Field
licence
Required
yes
Description
Who holds the data, how it is credited, and whether it may be kept.

PluginSource

One file Kanboombo downloads before each run and hands to your fetcher. https only; the host refuses private addresses and redirects to hosts no source names. {today}, {year} and {month} are filled in the manifest's time zone.

Field
Type
Required
Description
Field
name
Required
yes
Description
The source's name: the key your fetcher finds it under in the run context's sources.
Field
url
Type
string; at most 500 characters
Required
yes
Description
Where to download it: https, with a lowercase host.
Field
maxBytes
Type
integer; 1 to 20,971,520
Required
Description
The largest download you expect, at most 20 MB (the default). A larger file fails the run.

PluginSeries

One kind of record your plug-in writes, and how readers see it.

Field
Type
Required
Description
Field
key
Required
yes
Description
The series key every record of it names.
Field
kind
Required
yes
Description
value, event or item.
Field
cadence
Required
yes
Description
How often it has a new record.
Field
dates
Required
yes
Description
Whether a record is dated by what it describes (observation) or by when it was published (publication).
Field
name
Required
yes
Description
Its name per language, shown as the row's label.
Field
unit
Type
string; at most 16 characters
Required
Description
What its numbers are in, such as DOP, m or %. Up to 16 characters. Screen readers say it after the figures; with % a change is shown in basis points and an event's value as a percentage.
Field
fields
Type
array of PluginKey
Required
Description
A value series' numbers, by key, in the order they are shown. 1 to 16.
Field
fieldNames
Type
object of PluginNames
Required
Description
A label for each field, per language, for example compra: { es: Compra, en: Buy }.
Field
change
Required
Description
The field whose change from the previous record is shown, never coloured as good or bad.
Field
snapshot
Required
Description
A window each run fully restates: a stored record missing from it for three runs is withdrawn.

PluginSnapshot

Each run's batch is the whole truth for this window. A stored record inside it that is missing from three runs in a row is withdrawn.

Field
Type
Required
Description
Field
back
Required
yes
Description
How far before today the window reaches.
Field
ahead
Required
yes
Description
How far after today it reaches, for scheduled events.

The hosts an item record may link to, besides every source's host.

Field
Type
Required
Description
Field
hosts
Type
array of string
Required
yes
Description
Lowercase host names, such as www.bancentral.gov.do. At most 16.

PluginLimits

Lower limits than the defaults, if your fetcher needs less. They can never be raised.

Field
Type
Required
Description
Field
seconds
Type
integer; 1 to 120
Required
Description
How long a run may take, 1 to 120. Default 120.
Field
megabytes
Type
integer; 16 to 256
Required
Description
How much memory a run may use, 16 to 256. Default 256.

PluginLicence

Who holds the data and what you may do with it. Readers see the attribution beside every record.

Field
Type
Required
Description
Field
holder
Type
string; 1 to 200 characters
Required
yes
Description
Who publishes the data, such as Banco Central de la República Dominicana.
Field
attribution
Type
string; 1 to 40 characters
Required
Description
The short name shown beside its records, such as Banco Central.
Field
terms
Type
string or null; at most 500 characters
Required
yes
Description
A link to the terms the data is published under, or null when there are none to link.
Field
retain
Type
"yes", "no", "unknown"
Required
yes
Description
Whether Kanboombo may keep the records: yes, no (they can be purged) or unknown.

PluginId

A plug-in's id: lowercase letters, digits and hyphens, starting with a letter, 2 to 48 characters. It is also its folder's name, and it is never reused.

  • •
    Type: string
  • •
    Pattern: ^[a-z][a-z0-9-]{1,47}$
  • •
    Example: dop-economy

PluginKey

A key for a source, a series or a field: lowercase letters, digits, hyphens and underscores, starting with a letter.

  • •
    Type: string
  • •
    Pattern: ^[a-z][a-z0-9_-]{0,47}$
  • •
    Example: usd-dop, compra

PluginNames

A name in each language the plug-in speaks, keyed by language code. A reader sees their own language, then en, then the first listed.

  • •
    Type: object of string

PluginAudience

public: anyone signed in, and a Mac's proxy. private: the readers set at host.kanboombo.com (people and organizations). local: runs on your Mac only; refused by the Server until its slice ships.

  • •
    Type: "public", "private", "local"

PluginCadence

How often the series has a new record. It tells readers what to expect; it does not schedule anything.

  • •
    Type: "business-day", "daily", "weekly", "monthly", "quarterly", "irregular"

PluginRecordKind

value: numbers on a date. event: something scheduled, happened or cancelled. item: a dated link with a title.

  • •
    Type: "value", "event", "item"

PluginDates

observation: a record's date is the day it describes. publication: the day it was published, with period naming what it describes.

  • •
    Type: "observation", "publication"

PluginWindow

A span relative to the run's today: a number and a unit, d for days or y for years.

  • •
    Type: string
  • •
    Pattern: ^[0-9]{1,4}[dy]$
  • •
    Example: 60d, 400d, 5y

Limits

The checker's numbers, the same in check and on the Server:

Limit
Value
What it holds
Limit
sources
Value
16
What it holds
Sources one manifest may declare
Limit
sourceBytes
Value
20,971,520 (20 MB)
What it holds
The largest download one source may be (maxBytes), in bytes
Limit
sourceUrl
Value
500
What it holds
Characters in a source's url
Limit
series
Value
64
What it holds
Series one manifest may declare
Limit
fields
Value
16
What it holds
Fields in a value series, and in one value record
Limit
linkHosts
Value
16
What it holds
Hosts in links.hosts
Limit
name
Value
80
What it holds
Characters in a name, in each language
Limit
unit
Value
16
What it holds
Characters in a series' unit
Limit
attribution
Value
40
What it holds
Characters in licence.attribution
Limit
holder
Value
200
What it holds
Characters in licence.holder
Limit
terms
Value
500
What it holds
Characters in licence.terms
Limit
lines
Value
250,000
What it holds
Records one run may print
Limit
period
Value
32
What it holds
Characters in a record's period
Limit
id
Value
64
What it holds
Characters in an event's or an item's id
Limit
title
Value
200
What it holds
Characters in a title
Limit
url
Value
1,000
What it holds
Characters in an item's url
Limit
seconds
Value
1 to 120; 120 if you set nothing
What it holds
How long one run may take, in seconds (limits.seconds)
Limit
megabytes
Value
16 to 256; 256 if you set nothing
What it holds
How much memory one run may use, in megabytes (limits.megabytes)

What the Server holds a run to

What
Number
What
Time and memory
Number
120 seconds and 256 MB. A lower limits.seconds is kept; a lower limits.megabytes is not yet enforced on the Server, though check enforces both
What
Runs a day
Number
At most 24 (PLG-SCHEDULE-TOO-FREQUENT at upload)
What
A withdrawal
Number
A stored record missing from its snapshot window for 3 runs in a row
What
Withdrawals that wait for a person
Number
More than 10 at once, or more than 1% of a window of 100 or more
What
Days a reader asks for at once
Number
62
What
Records one run may print
Number
250,000
What
The time zone
Number
A named IANA zone, up to 100 characters

Contract 1 and its changes

Contract 1 only grows: a field, a stated limit or a check with its own code may be added, and nothing a plug-in relies on is taken away or changes meaning. A change that would break a working plug-in is contract 2, run beside contract 1. A check code is never renamed or reused; a retired one stays in Submission errors.

October 2026. Contract 1, with DOP Economy on readers' days. Added the same week, changing nothing DOP Economy writes nor its digest: $schema (plugin.schema.json); links.hosts; backfillFrom in the run context; fixtures/context.json and fixtures/runs/<case>/; the digest leaving out listing.json, listing/, review.json, .git/, .DS_Store, AGENTS.md and CLAUDE.md; and a code from one catalog, with its fix and page, on every problem. The tool's versions are in the CLI.