# Writing a day: fields, correcting one, and publishing

> Write one day as a draft, correct it, and put it on the site.

## Writing a day

A day's slug is client-chosen — `YYYY-MM-DD-slug` — and you `PUT` it into existence:

```http
PUT /api/v2/{user}/trips/{trip}/days/{slug}
Content-Type: application/json
Authorization: Bearer fs_agent_…
```

**Every field a day may carry:** `slug` (required), `title` (required), `date` (required), `content` (required), `media`, `costs`, `coordinates`, `weather`, `time`, `timezone`, `location`, `country`, `countryCode`, `transportMode`, `tags`, `translations`, `visibility`, `status`, `declined`, `transportFrom`, `transportTo`, `travelScene`, `test`. `title`, `date` and `content` are always required. Full descriptions — what `travelScene` plays, what `weather` accepts — are at `/api/v2/openapi.json` under this path's `PUT` request.

**Everything else is asked, or declined.** `media`, `costs`, `coordinates`, `weather`, `time`, `timezone`, `location`, `country`, `countryCode`, `transportMode`, `tags`, `translations` (only where the journal keeps more than one language) and `visibility` are each either sent, or named in `declined` with a real reason — v1's per-field true/false/unrecorded encoding is retired; write `declined: {"costs": "nothing was spent this day"}` instead. Never invent a value to satisfy this, and never reach for a decline just to get past a refusal — ask.

**A country gets its code for free.** A day that names a `country` the server can place comes back carrying the matching ISO-3166 `countryCode` too, even when you did not send one — that code is what a photobook prints its chapter titles from, in the book's own language, and it happens before the asked-or-declined check, so naming a country does not oblige you to decline its code as well. A name the server cannot place, or a day that has declined `countryCode`, is left exactly as you sent it: nothing is guessed, and an unplaceable name is still asked for. Send the code yourself whenever you know it.

**Weather has exactly two honest routes.** `weather: true` asks this server to look the day's weather up itself, from its own `coordinates` and `date` — never guess one. The lookup happens inside this write, so the day it answers with already carries the reading. A public archive lags real time, so a day written the evening it happened can come back with `weather` still `true` and no reading: that is "not yet", not a failure, and nothing on this server comes back for it — send `weather: true` again later and it asks the archive again. A reading already on the day is never overwritten by asking. The other route is the same `weather` field carrying an object instead of `true` — a reading somebody actually took, naming a `source` and a `recordedAt`; `open-meteo` is refused as that source because that name means the server looked it up.

**It always arrives as a draft.** `status` accepts only `"draft"` — publishing is a separate call, below, and it is never a side effect of writing or correcting a day.

```json
{"title": "Lanterns of Hoi An", "date": "2026-08-26", "content": "…", "status": "draft", "coordinates": {"lat": 16.0471, "lng": 108.245}, "location": "Hoi An Ancient Town", "country": "Vietnam", "time": "19:30", "timezone": "Asia/Ho_Chi_Minh", "transportMode": "walk", "tags": ["lanterns"], "weather": true, "declined": {"media": "no photographs uploaded for this day yet", "costs": "cash, nobody kept the receipts", "countryCode": "left for the server to place", "translations": "single-language journal", "visibility": "shown to everyone the trip lets in"}}
```

## Correcting a day

```http
PATCH /api/v2/{user}/trips/{trip}/days/{slug}
```

A merge-patch: send only what changed. Nothing is required — attaching one photograph must not re-open every question — but a field cannot be both sent and declined in the same patch, and sending a field that was previously declined clears that decline. `status` is never accepted here: a draft you correct stays a draft, and a published day you correct stays published and visible to whoever already read it. A draft may stay incomplete (a day the person started in the studio often is, and may have no title yet): publishing asks for what is missing. A published day must stay complete — a correction that would leave it incomplete answers `422 incomplete`. If the day changed while your patch was being checked — it was published, or somebody else saved — the answer is `409 stale_document` and nothing was written: read it again and resend.

## Publishing, when they say so

```http
POST /api/v2/{user}/trips/{trip}/days/{slug}/publish
Content-Type: application/json
Authorization: Bearer fs_agent_…

{"sendMail": true}
```

Owner only — a trip-scoped token writes days and cannot put them on the site. `declineTracked` lets a publish decline a trip-tracked fact the day genuinely has none of — only a field the day has neither filled in nor already declined; naming any other refuses the whole call with `400` and `details.refused`, nothing written. `sendMail`/`sendWhatsapp` each default to absent, since publishing fifteen days must never default to fifteen letters. Ask before setting either to `true`. A `402` means the balance cannot cover the send — the day stays a draft and nothing is sent; hand over a `PUT /api/v2/{user}/purchases/{id}` link rather than retrying. A `409 stale_document` means the day changed while the publish was checked; nothing was published or sent — read it again and ask before publishing what is there now.

`POST /api/v2/{user}/trips/{trip}/days/{slug}/unpublish` takes the day down — reversible, nothing deleted. `POST /api/v2/{user}/trips/{trip}/days/{slug}/send` sends (or resends) a published day on named `channels` (`["mail"]`, `["whatsapp"]`, or both) — never idempotent, so ask again in words before calling it twice.

## Channels — whether either can send anything at all

```http
GET   /api/v2/{user}/channels
PATCH /api/v2/{user}/channels
{"mail": true, "whatsapp": false}
```

Mute switches for the owner's own sending channels — `null` for a channel this instance does not offer at all, so a switch that cannot exist never reads back as a confident `false`. Off means `POST /api/v2/{user}/trips/{trip}/days/{slug}/send` on that channel refuses rather than sending.

## Deleting

```http
DELETE /api/v2/{user}/trips/{trip}/days/{slug}
```

A **draft** day deletes outright — no confirmation, since it was never on the site; its media is kept. A **published** day cannot be deleted through this door at all — `409 published_day_not_deletable` — `unpublish` first.
