The garden HTTP API
garden serves maintained engineering guidance as immutable publications, called snapshots. The HTTP API reads them. It is read-only: every request is a GET, and nothing a request sends is stored as content.
Availability today. garden's hosted endpoint is https://garden-api.fernworks.dev/. It serves current and historical publications to a valid access key. There is no public sign-up.
Requests#
Every request carries an access key as a bearer credential:
GET /v1/snapshots/current HTTP/1.1
Host: garden-api.fernworks.dev
Authorization: Bearer <your key>
The key is the only credential. garden sets no cookie and asks for no other header. Keep the key out of URLs, logs and files you share: garden reads it only from the Authorization header, and never echoes it in an answer.
Request headers may total at most 16,384 bytes. Query parameters are accepted by the list read and the comparison only.
The reads#
A publication is named by its exact id, or by current, which garden resolves to the exact id of the publication current at that moment.
| Request | Answer |
|---|---|
GET /v1/snapshots/current | the current publication: { "id": …, "publishedAt": … } |
GET /v1/snapshots/:snapshot | that exact publication, in the same form; a missing id is not found, never current |
GET /v1/snapshots/:snapshot/vocabulary | every value a facet, kind or responsibility may carry in that publication |
GET /v1/snapshots/:snapshot/units | the units matching the filters: their listing metadata, in id order, with the exact count |
GET /v1/snapshots/:snapshot/units/:id | one unit's family and exact text, with its revision and content hash |
GET /v1/snapshots/:snapshot/anchors/:anchor | the id of the unit that carries the anchor, as a JSON string |
GET /v1/snapshots/:to/changes?from=:from | the comparison of two exact publications by revision and content hash |
Resolve once, then read by the exact id#
Resolve current once, then name the exact id it answered in every later read. Each read then comes from that one publication, even if a newer one becomes current while you read. changes takes two exact ids and refuses current.
Listing units#
GET /v1/snapshots/:snapshot/units takes the filters language, purpose, technology, task, concern, kind and responsibility. A filter may be repeated: values of one filter are alternatives, and different filters must all hold. A unit that names no value for an applicability facet (language, purpose, task, concern) applies everywhere and matches any value of that facet; technology names what a unit is about, so a unit with no technology matches no technology filter. A value the publication's vocabulary does not hold is refused, and the refusal names the vocabulary's values. With no filter the listing holds every unit. There is no pagination and no truncation: the count is exact.
GET /v1/snapshots/<id>/units?language=typescript&task=implement HTTP/1.1
The listing carries each unit's id, kind, title, when it applies, summary, facets, option group, the units it requires and contains, related units, and its verification record. It never carries a unit's text.
Reading a unit#
GET /v1/snapshots/:snapshot/units/:id answers:
{ "family": "…", "content": "…", "revision": "…", "contentHash": "…" }
content is the unit's exact original text. contentHash is the SHA-256 of that text as UTF-8, and revision is the SHA-256 of JSON.stringify([family, content]), both in lowercase hexadecimal. Verify both before you use the text. A unit's front matter names the units it requires; read those too when the unit's instruction depends on them.
Comparing two publications#
GET /v1/snapshots/:to/changes?from=:from answers which units were added, changed and removed between the two publications, each with the revision and content hash before and after, a count, and whether the vocabulary changed. It delivers no unit text.
Response headers#
| Header | Meaning |
|---|---|
X-Garden-Protocol | the version of this HTTP contract, 1, on every answer |
X-Garden-Snapshot | the exact id of the publication a successful read came from |
Content-Type | application/json; charset=utf-8 |
Cache-Control | no-store |
Check X-Garden-Protocol on every answer and refuse any other version. Check that X-Garden-Snapshot names the publication you asked for.
Errors#
A refused request answers with a JSON body of one shape:
{ "error": { "kind": "…", "input": "…", "message": "…" } }
kind is the machine-readable reason, input names what was refused (a field, an id, an anchor, a publication), and message is for a person. The body never carries the key or any internal detail.
| Status | kind | Meaning |
|---|---|---|
| 400 | unknown facet, unknown value, unknown kind, unknown responsibility | a list filter garden does not know, or a value the publication's vocabulary does not hold |
| 400 | invalid request, invalid operation | a malformed request, a misplaced query parameter or a bad comparison |
| 401 | unauthorized | no key, or a key garden does not accept |
| 403 | forbidden | a key without a live entitlement |
| 404 | no snapshot, unknown id, unknown anchor, not found | the publication, unit, anchor or read named is not there |
| 405 | method not allowed | a method other than GET |
| 431 | invalid request | request headers over 16,384 bytes |
| 503 | unavailable | garden could not answer; try again later |
A missing exact publication is never answered with current.