> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lala.ist/llms.txt
> Use this file to discover all available pages before exploring further.

# Planner

> Read and write the study plan: blocks of work on a day, with or without a time.

The planner holds blocks of study work. One block is one thing to do: a subject, an optional
topic, and how much of the slot is decided.

A block belongs to a local Istanbul date. It carries a wall-clock time, not an instant. A device
with the wrong timezone still shows the plan the student made.

## What a block is

A block always has a date and a subject. The time and the length are separate, and each one can be
undecided:

| What the student said | `startTime` | `endTime` | `durationMin` |
| - | - | - | - |
| "tomorrow 09:00 to 10:30, maths" | `09:00` | `10:30` | — |
| "tomorrow 09:00, one hour of maths" | `09:00` | — | `60` |
| "45 minutes of maths tomorrow, time undecided" | — | — | `45` |
| "maths tomorrow, sometime" | — | — | — |

The response always holds both `startTime` and `durationMin`, and it adds `endTime` when both are
known. The `scheduled` field is `true` when the block has a time.

An `endTime` needs a `startTime`, and it wins over `durationMin`. An `endTime` before the
`startTime` is a block that runs past midnight. The block stays on the day it starts.

## Read a range

`GET /api/plan` returns every date from `from` to `to`. Empty days are in the response, so a
calendar renders straight off it. The default window is today and the next six days.

```bash theme={null}
curl "$BASE_URL/api/plan?from=2026-08-10&to=2026-08-16" \
  -H "Authorization: Bearer $LALA_TOKEN"
```

```json theme={null}
{
  "from": "2026-08-10",
  "to": "2026-08-16",
  "days": [
    {
      "date": "2026-08-10",
      "blocks": [
        {
          "id": "9f1c2d3e-4a5b-4c6d-8e7f-0a1b2c3d4e5f",
          "date": "2026-08-10",
          "startTime": "09:00",
          "endTime": "10:30",
          "durationMin": 90,
          "scheduled": true,
          "subject": "Matematik",
          "topic": "türev",
          "activity": "questions",
          "targetQuestions": 40,
          "notes": null,
          "status": "planned",
          "doneQuestions": null,
          "completedAt": null,
          "createdBy": "student",
          "position": 0,
          "createdAt": "2026-08-09T18:20:00.000Z",
          "updatedAt": "2026-08-09T18:20:00.000Z"
        }
      ],
      "plannedMinutes": 90,
      "unscheduledCount": 0,
      "doneCount": 0
    }
  ]
}
```

The blocks of a day come back in display order: blocks with a time first, in time order, then the
blocks with no time. The `position` field orders that second group.

The range is 62 days at most. Read a month at a time. There is no cursor, because a date range is
already a bounded read.

## Add blocks

`POST /api/plan` always takes an array. One block and a whole week are the same call, and the
batch lands whole or not at all.

```bash theme={null}
curl -X POST "$BASE_URL/api/plan" \
  -H "Authorization: Bearer $LALA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "blocks": [
      {
        "date": "2026-08-10",
        "startTime": "09:00",
        "endTime": "10:30",
        "subject": "Matematik",
        "topic": "türev",
        "activity": "questions",
        "targetQuestions": 40
      },
      { "date": "2026-08-10", "durationMin": 45, "subject": "Fizik", "activity": "tekrar" }
    ]
  }'
```

```json theme={null}
{
  "blocks": [{ "id": "9f1c2d3e-...", "subject": "Matematik", "durationMin": 90 }],
  "conflicts": []
}
```

### Activity

`activity` says what the block is. The values and their Turkish labels:

| Value | Label |
| - | - |
| `questions` | soru çözümü |
| `lesson` | konu anlatımı |
| `review` | tekrar |
| `subject_mock` | branş denemesi |
| `mock_exam` | deneme |
| `homework` | ödev |
| `reading` | okuma |
| `break` | mola |
| `other` | çalışma |

On write, the Turkish label works too. `"activity": "branş denemesi"` and
`"activity": "subject_mock"` store the same value. A response always holds the English value.
An unknown activity is a `400`.

The server also folds subject spellings, so `mat`, `Mat` and `MATEMATİK` all become `Matematik`.
A subject that is not in the list survives as the student typed it.

### Conflicts

Two blocks that overlap in time come back in `conflicts`:

```json theme={null}
{
  "blocks": [{ "id": "3c4d5e6f-...", "subject": "Fizik" }],
  "conflicts": [{ "blockId": "3c4d5e6f-...", "conflictsWith": ["9f1c2d3e-..."] }]
}
```

The write is never refused for an overlap. The student can mean it. Show the overlap and let the
student decide.

Blocks that touch do not conflict. A block that ends at 10:00 and a block that starts at 10:00 are
two separate blocks.

## Change a block

`PATCH /api/plan/{id}` writes only the fields in the body. Timing merges onto what is stored:

| Body | Result |
| - | - |
| `{"startTime":"14:00"}` | The block moves to 14:00 and keeps its length. |
| `{"startTime":null}` | The time comes off. The day and the length stay. |
| `{"date":"2026-08-12"}` | The block moves to another day. The time stays. |
| `{"durationMin":120}` | The length changes. The start stays. |

Tick a block off with `status`:

```bash theme={null}
curl -X PATCH "$BASE_URL/api/plan/9f1c2d3e-4a5b-4c6d-8e7f-0a1b2c3d4e5f" \
  -H "Authorization: Bearer $LALA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status":"done","doneQuestions":38}'
```

The server stamps `completedAt` when the status becomes `done`, and clears it when the status goes
back to `planned`. The field is read-only.

Use `skipped` for a block the student did not do. The block stays in the plan, and it stays out of
the conflict check.

## Remove a block

`DELETE /api/plan/{id}` returns `{"id":"...","deleted":true}`. A second delete of the same id
returns `deleted: false`.

<Note>
  A deleted block leaves no trace. To record that the student did not do the work, set the status to
  `skipped` instead.
</Note>

## Lala and the plan

Lala reads the plan on every turn: yesterday and the next six days. It can add blocks, move them,
and mark them done from the chat. A block that Lala created has `createdBy: "lala"`.

The student can ask for this directly. "Put maths on Friday" and "maths is done" both work in
chat, and the change is in the plan before the reply arrives. Read the plan again after a chat
turn, or listen on `GET /api/chat/stream` and read after each message.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.