# Journal

> https://meetkeely.com/developers/api/journal · Updated 2026-11-06 · Keely, by Metrica Fit LLC

## GET /v1/journal/tags

- **Scope:** `journal`
- **Errors:** `409 journal_off` while the person isn’t sharing their journal

The journal’s tags: the built-in ones and the person’s own, with what each means. `can_log` says whether an app may log the tag; some are the person’s or Keely’s own measurements to log.

```json Response
{
  "tags": [
    { "key": "alcohol", "label": "Alcohol", "category": "foodDrink", "category_label": "Food and drink",
      "summary": "Any alcoholic drink: beer, wine, spirits, cocktails.", "built_in": true, "can_log": true, "status": "active" },
    { "key": "magnesium", "label": "Magnesium", "category": "recovery", "category_label": "Recovery practices",
      "summary": "Magnesium before bed", "built_in": false, "can_log": true, "status": "active" }
  ],
  "as_of": "2026-09-27T15:20:00Z",
  "methodology": "https://meetkeely.com/methodology"
}
```

## POST /v1/journal/tags

- **Scope:** `journal:write`
- **Body:** `label` (1 to 40 characters), `category` (one of `foodDrink`, `sleepSchedule`, `training`, `recovery`, `unwell`, `environment`, `life`, `other`), `summary` (one line of up to 200 characters)
- **Errors:** `422 journal_refused` with `reason: tag_cap` past 50 tags of the person’s own; `409 journal_off`

Makes a tag of the person’s own, when they ask your app to track something none of their tags covers. When a tag already says the same thing, Keely returns it with `"existing": true` rather than making another. A person has at most 50 tags of their own.

```json Request body
{ "label": "Magnesium", "category": "recovery", "summary": "Magnesium before bed" }
```

```json Response
{
  "tag": { "key": "magnesium", "label": "Magnesium", "category": "recovery", "category_label": "Recovery practices",
    "summary": "Magnesium before bed", "built_in": false, "can_log": true, "status": "active" },
  "existing": false,
  "as_of": "2026-09-27T15:20:00Z",
  "methodology": "https://meetkeely.com/methodology"
}
```

## GET /v1/journal/entries

- **Scope:** `journal`
- **Query:** `from`, `to` (`YYYY-MM-DD`), at most 92 days; the last 14 by default
- **Errors:** `409 journal_off` while the journal isn’t shared

The journal over local dates: what the person noted in the app, what Keely noticed (a hard session, a rest day, travel) and what an assistant or an app added at their request.

```json Response
{
  "from": "2026-09-14",
  "to": "2026-09-27",
  "count": 2,
  "entries": [
    { "id": "8c1e2d3f-0a4b-4c5d-8e6f-7a8b9c0d1e2f", "tag": "alcohol", "label": "Alcohol", "category": "foodDrink",
      "start": "2026-09-26T16:20:00Z", "end": "2026-09-27T00:20:00Z", "day": "2026-09-26",
      "pattern": "once", "pattern_label": "once", "count": null,
      "source": "tap", "basis": null, "can_delete": false, "changed_at": "2026-09-27T06:50:00Z" },
    { "id": "inferred.hard_session.5f3c0e1d2b4a6978", "tag": "hard_session", "label": "Hard session", "category": "training",
      "start": "2026-09-27T09:20:00Z", "end": "2026-09-27T10:14:00Z", "day": "2026-09-27",
      "pattern": "once", "pattern_label": "once", "count": null,
      "source": "inferred", "basis": "workout", "can_delete": false, "changed_at": "2026-09-27T15:20:00Z" }
  ],
  "as_of": "2026-09-27T15:20:00Z",
  "methodology": "https://meetkeely.com/methodology"
}
```

- **`source`** is `tap` (the person, in the app), `text` (the person, in their own words), `inferred` (Keely noticed it, from `basis`: a workout, an activity, the day’s Load or a time-zone change), `assistant` or `app`. **`can_delete`** is true only for what an app added.
- **`pattern`** is `once`, `occasional`, `mostDays`, `daily` or `count` (with `count`), and `pattern_label` says it in words. An entry with `end: null` is ongoing.

## POST /v1/journal/entries

- **Scope:** `journal:write`
- **Body:** `tags`, one to five keys from the tags list, and either `when` or `date`
- **Answers:** `201` with what was added
- **Errors:** `422 journal_refused` with a `reason`; `409 journal_off`

Adds entries, only when the person asks your app to log something about themselves:

- `when`: `last_night` (18:00 yesterday to 10:00 today, in the person’s time zone), `today`, `yesterday`, `past_few_days`, `past_week`, `past_month` (on some of those days, not specific ones), `ongoing` (from today on, every day), or `tomorrow`, which is for a planned race only (`tags: ["race"]`).
- `date`: one local date, within the last 90 days, for that whole day.

```bash Request
curl -X POST https://meetkeely.com/api/v1/journal/entries \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "tags": ["late_caffeine"], "when": "today" }'
```

The rules are the database’s, the same for assistants and apps: only tags an app may log, only times in the past 90 days, at most 50 entries a day from apps and assistants together, and a duplicate of an entry that already covers the window isn’t added again.

```json Response
{
  "entries": [
    { "id": "b7a1c2d3-e4f5-4061-8293-a4b5c6d7e8f9", "tag": "late_caffeine", "start": "2026-09-27T00:00:00Z", "end": "2026-09-27T15:20:00Z",
      "pattern": "once", "source": "app" }
  ],
  "as_of": "2026-09-27T15:20:00Z",
  "methodology": "https://meetkeely.com/methodology"
}
```

Log only what the person asked to log, about themselves, and confirm it to them in a line. Never suggest logging, and never log moods, health conditions, other people or what a conversation was about.

## DELETE /v1/journal/entries/{id}

- **Scope:** `journal:write`
- **Path:** `id`, the entry’s
- **Answers:** `200` with `{ "id": "…", "deleted": true }`
- **Errors:** `422 journal_refused` with `reason: not_added_by_an_app` for what the person added in the app, or Keely noticed; `404 not_found` for an id that doesn’t exist

Deletes an entry an app added, when the person asks to undo it. What the person added in the app, or Keely noticed, is theirs to change in the app.
