Skip to content

Watering Availability API

The Watering API lets community members anonymously register their availability for watering shifts. After a collection period, the garden coordinator triggers plan calculation — the LetItRain algorithm distributes tasks fairly and the result is stored as a queryable watering plan.

Architecture

Frontend Wizard  →  POST /api/watering/availability  →  Oxigraph (SPARQL)
                                                             ↓
                      POST /api/watering/plan/calculate  →  LetItRain algorithm
                      GET  /api/watering/plan            ←  stored WateringPlan

The watering-api service is a Bun/Hono process served under /api on the same domain as the frontend. It uses the Graviola SPARQL store interface to persist data directly to the Oxigraph triple store.

No authentication

Users participate anonymously. The frontend generates a UUID per device (stored in localStorage) and includes it as personId in every submission. The API has no login, no session, no registration.

Endpoints

POST /api/watering/availability

Submit or update a user's watering availability for a specific month.

Request body:

{
  "gardenIri": "https://data.semantic-desk.top/garden/Garden/Wurzelwerk",
  "personId": "550e8400-e29b-41d4-a716-446655440000",
  "maximumTasksPerMonth": 2,
  "availableDates": ["2026-06-02", "2026-06-05", "2026-06-09"],
  "targetMonth": "2026-06"
}

Response: 201 Created with the stored entity including its @id.

Re-submitting with the same personId + gardenIri + targetMonth replaces the previous entry.

GET /api/watering/availability?gardenIri=<IRI>[&month=YYYY-MM]

List availability entries for a garden. Pass month to filter to a specific month.

DELETE /api/watering/availability/:id

Remove an entry (e.g. user withdraws).

POST /api/watering/plan/calculate

Run the LetItRain assignment algorithm and persist the result for a calendar month.

Request body:

{
  "gardenIri": "https://data.semantic-desk.top/garden/Garden/Wurzelwerk",
  "targetPersonsPerTask": 2,
  "targetMonth": "2026-06"
}

targetMonth defaults to next month when omitted.

Response: 200 OK with the full WateringPlan including assignments[] and targetMonth.

GET /api/watering/plan?gardenIri=<IRI>[&month=YYYY-MM]

Retrieve the calculated plan for a garden. month defaults to the current calendar month.

Data model

WateringAvailability

Stored at https://data.semantic-desk.top/garden/WateringAvailability/{gardenSlug}-{personId}-{YYYY-MM}.

One document per person per garden per calendar month — re-submitting replaces it.

Field Type Description
gardenIri IRI string Which garden this submission is for
personId string Anonymous participant identifier
maximumTasksPerMonth integer How many times per month the person is willing to water
availableDates string[] ISO dates (YYYY-MM-DD) the person can water
targetMonth string Calendar month (YYYY-MM) this entry covers
submittedAt ISO datetime Server-assigned timestamp

WateringPlan

Stored at https://data.semantic-desk.top/garden/WateringPlan/{gardenSlug}-{YYYY-MM}.

One plan per garden per calendar month — recalculating replaces the stored plan.

Field Type Description
gardenIri IRI string Garden this plan covers
targetMonth string Calendar month (YYYY-MM) this plan covers
calculatedAt ISO datetime When the plan was generated
validFrom ISO date First task date in the plan
validUntil ISO date Last task date in the plan
assignments { date, personIri }[] One entry per assigned watering shift

Running locally

The service starts automatically with the dev stack:

docker-compose -f docker-compose.dev.yml up -d

Check it's up:

curl https://dev01.pergola.kuenste.live/api/watering/health

E2E test

The Cypress spec e2e/cypress/e2e/watering-api.cy.ts covers the full flow: submitting fixture entries → listing → calculating → reading the plan.

./scripts/run-cypress.sh