Documentation

Keely for developers

Journal

The person’s journal, only while they share it in the Keely app: its tags and entries to read, and entries to add when they ask your app to.

Updated

On this page
  1. GET /v1/journal/tags
  2. POST /v1/journal/tags
  3. GET /v1/journal/entries
  4. POST /v1/journal/entries
  5. DELETE /v1/journal/entries/{id}

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.

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.

Request body
{ "label": "Magnesium", "category": "recovery", "summary": "Magnesium before bed" }
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.

  • 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.
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"
}

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.

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.

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.

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" }'
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"
}

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.