Blueprints for builders
Every MyStatus task is already readable by people and machines. Humans know how to make them readable by humans, utilizing the writer's style and flavor. This page helps with the flip side: helping machines know how to make them readable by other machines, utilizing the writer's style and flavor.
What every task already gives a machine
Before any convention of yours, the floor. These are true of every task on the site, yours and everyone else's, and nothing below works without them.
- Three fields come back structured, always. The label chip, the timestamp the update claims it happened (
occurred_at, which is also what the stream is ordered by), and any link attachments. Those are columns, not prose — a reader never has to guess at them, and never has to parse a body to get at them. - Every update has its own address.
/t/{token}/u/{updateId}— the task page, focused on that one update, linkable from anywhere. It dies with the token and not before. - Every task has an Atom feed. If polling a feed is all you need, you are already done; you can stop reading here.
- Nothing expires. There is no retention window and no archive tier. An update you read today is one you can read in five years, at the same address.
The blueprint pattern
A blueprint is a contract you write in your task's description, under a heading that says Required data structures. It tells readers of the task how to treat its updates as data. It is prose in a text field — there is no schema registry, no new MyStatus markup language flavor, no upload step, nothing to register with us. You write down what your rows mean, and then you mean it.
And if you post an update that breaks the agreement, no biggie. Nothing breaks, but nothing is extracted either.
Declare whichever format suits you: a JSON block, key-value lines, or one entry per update where the whole body is the value. The examples below use key-value lines, which is the least ceremony for something a human also has to read.
- A row is one update read as data. Not a paragraph, not a section: one update, one record. A change that touches two things posts two updates, and a reader gets two rows.
- The key-value block ends at the first blank line. Everything after the blank line is prose for humans. A parser stops at the blank line and never has to understand the rest.
- A key-value value runs to the end of its line, by definition. That is the whole reason the format cannot be confused. There is no continuation character and no escape for a newline inside a value, because there does not need to be — see the three homes for multiline content below.
- An update that doesn't parse is prose, and that's fine. Not an error, not a warning. Updates older than your contract are prose by definition, and a reader skips them. This is what lets you add a contract to a task that has been running for a year without rewriting its history.
- Writers may add keys at any time. If you're writing a reader, take the keys you want and skip the rest: an unfamiliar key is normal, not an error. A reader that fails on one is a reader that breaks the first time somebody else finds your task useful.
Where multiline content goes. Three sanctioned homes, and only three. In the prose section below the blank line, where it belongs most of the time. In a JSON-block contract, where strings escape their own newlines. Or in a one-entry-per-update contract, where the whole body is the value and there is nothing to parse around it.
A promise about line endings. We standardize on \n, no matter what you send us. Be prepared to handle \n in reads even if you passed in \r. That is true of every update in the system, not just new ones — the day we started normalizing, we rewrote everything already stored, so there are no older updates to point back at as exceptions.
Field caps are your contract's business, not ours. If your blueprint says a field is <text, ≤1000 chars>, that is a rule for your writers and your readers; we do not enforce it. The only hard cap the platform imposes today is 5,000 characters for an update body. Expect that number to become plan-dependent as tooling needs grow — write your contract against your own limits rather than against ours.
The notation, which is a suggestion. The live examples write | for one-of and <…> for a described value. It reads well and costs nothing to copy. It is guidance by example — there is no notation you are required to use, because there is nothing on our side reading it.
Changing a contract. Adding a key is free — that is what must-ignore buys you. A breaking change, meaning a renamed key or a changed meaning, closes the task with a link card to its successor, whose first entry links back. The old task keeps answering forever at its old address with its old updates intact. Immutability by chaining, rather than by locking anything down.
The stream is the contract's changelog. The description holds the contract's current truth; the task's own stream holds its history. So when you add a key, do it in that order: update the description first, then post an update announcing the change. The description never lags the stream. A reader who wants to know which fields existed when walks the stream; everybody else is covered by must-ignore and never has to care.
Reading by machine
Public reads go through three Postgres functions, over ordinary HTTP GET. They are what this site's own pages call, so they are as live as anything gets.
- get_public_task — a task's metadata, its parent, and its children, given a share token. This is where the description lives, which means it is where a blueprint lives.
- get_public_updates — the stream, one page at a time. Pass
p_limit, then pass the last row'sseqback asp_beforeto get the next page. Repeat until a page comes back short. - get_public_update_window — the rows around one update, which is what a permalink page needs and what you want if you are resolving a single row rather than walking a task.
Calls look like this, and the key below is the publishable one — it is in the source of every page on this site by design, and it grants exactly what an anonymous reader already has.
curl "https://wvuifaudjzagekmlkjag.supabase.co/rest/v1/rpc/get_public_updates?p_token=YWvAnJB8u1UraEQx&p_limit=100" -H "apikey: sb_publishable_13Wx-m4BVRP2wUoJxGB8ow_8rcGWnHM"Versioning, so you can build against these. We add freely — new fields, new keys — and your reader must-ignores past them, same as anyone reading yours. What we don't do is reshape what you already parse: a breaking change ships under a new name, so the name you are calling keeps answering.
Two tasks already running it
Both of these are ours, both are live, and neither contract is written out on this page — they live in the task descriptions, where they are versioned. Follow the links and read the real thing.
- The MyStatus Changelog →Read its key vocabulary — a short, closed set of kinds, and a surface that carries a route when there is one. It is the smallest useful blueprint: two promised keys, and everything else along for the ride.
- The MyStatus Release Ledger →Watch what happens to the entries that predate its contract: they degrade to prose, exactly as designed, and a parser walks straight past them. That is the pattern's most useful property and you can see it working on a real task rather than take our word for it.
Sample code to play with
A short working reader, about forty lines: fetch a task, page through it, parse the key-value block, skip what doesn't parse. It reads the real Changelog above. Fork it and point it at your own task.
What we deliberately don't do
We do not validate your rows. There is no schema check, no linter, no warning when a row misses a key your own description promised. Nothing on our side reads your contract at all — it is a text field, and the parser is whoever you wrote it for.
That is a choice, not an omission. Validation means we would have to understand your format, which means you would have to write it in ours, which is the moment a free-form stream stops being one. We provide the forum; you provide the content. We won't check your rows — but we're building a paid tool that helps you write blueprints worth keeping.
Free and paid, plainly
All accounts include unlimited regular tasks (the default) and ten tasks with API keys enabled, which is enough to build against and to preview the tooling properly before you decide. After that, you'll need a paid account to add the 11th+. How we came to that 10 number.
Our philosophy towards who to charge is very simple:
- Free accounts are for human writers tracking things in their lives: output designed to be read by humans, but can be read by machines.
- Paid accounts are primarily machine writers tracking things in their workflows: output designed to be read by machines, but can be read by humans.