# Get a practice session's next cards

Source: https://workroad.app/docs/api/get-practice-session

`GET https://workroad.app/api/v1/practice-sessions/{sessionId}`

Returns the cards still waiting for an answer. When none are left, reading the session refills it with up to 30 more, decided by the server clock, and drops cards that were reviewed or suspended elsewhere since. `finished: true` means nothing is due and today's new cards are spent; read it again at `nextDueAt`. Reading never records an answer, so it is safe to repeat.

- MCP tool: `getPracticeSession` on `https://workroad.app/api/mcp`
- Required scopes: activity:view and sets:view
- Test mode: `GET https://workroad.app/api/test/v1/practice-sessions/{sessionId}` checks the request and returns the example response without changing anything.
- Next: answer each card with `answerPracticeCard`; when `finished` is true, stop and come back at `nextDueAt`.

## Path parameters

- `sessionId`: string (uuid), required. Practice session id from `startPracticeSession`, scoped to the key's user.

## Response

`200` OK

- `data`: object, required
  - `id`: string (uuid), required. Pass as `sessionId` to the other practice tools.
  - `cram`: boolean, required
  - `setIds`: array of string, required. The sets the session is limited to. Empty means every set.
  - `subjectId`: string or null, required. The subject it is limited to.
  - `createdAt`: string (date-time), required. When the session started.
  - `expiresAt`: string (date-time), required. When the session stops accepting answers, 7 days after it started. Start a new one then.
  - `cards`: array of objects, required. Cards to answer now, in order, up to 30: learning and relearning first, then reviews, then new cards, taking turns between sets. Answer them with `answerPracticeCard`, then read the session again to refill.
    - `itemId`: string (uuid), required. Send this as `itemId` to `answerPracticeCard`.
    - `cardId`: string or null, required. The FSRS card. Null in a cram session for a term never practised, which gets no card.
    - `termId`: string (uuid), required. The term's id in its study set.
    - `setId`: string (uuid), required
    - `setName`: string, required
    - `subjectId`: string or null, required. The set's subject, when set.
    - `activity`: one of `"flashcards"`, `"memory"`, `"match"`, `"guess_the_term"`, `"sentence_scramble"`, required. How the app quizzes this card: `flashcards` (recall the definition), `memory` (fill the blanks), `match`, `guess_the_term` (pick the term for the definition) or `sentence_scramble`. An agent can quiz every kind by asking for the definition.
    - `term`: string, required
    - `definition`: string, required
    - `hints`: array of string, required
    - `imageUrl`: string or null, required. The term's image, when it has one.
    - `queue`: one of `"new"`, `"learning"`, `"relearning"`, `"review"`, required. The card's scheduling queue.
    - `due`: string (date-time), required. When the card became or becomes due.
  - `answeredCount`: integer, required. Answers recorded in this session.
  - `finished`: boolean, required. True when nothing is due now and today's new-card limit is spent. The session stays open: read it again at `nextDueAt`.
  - `nextDueAt`: string (date-time) or null, required. When `finished` is true, the next time a card in the session's scope becomes due. Null otherwise.
  - `undoableItemId`: string or null, required. The newest answer that `undoPracticeAnswer` can take back. Null in a cram session or when nothing can be undone.
  - `serverTime`: string (date-time), required. The server clock that decided which cards are due.

### Example

```json
{
  "data": {
    "id": "8b7a6f5e-4d3c-4b2a-9f1e-0d9c8b7a6f5e",
    "cram": false,
    "setIds": [],
    "subjectId": "5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f",
    "createdAt": "2026-02-01T12:00:00.000Z",
    "expiresAt": "2026-02-08T12:00:00.000Z",
    "cards": [
      {
        "itemId": "1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b",
        "cardId": "3e2d1c0b-9a8f-4e7d-8c6b-5a4f3e2d1c0b",
        "termId": "2a1b0c9d-8e7f-4a6b-9c5d-4e3f2a1b0c9d",
        "setId": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b",
        "setName": "Spanish verbs",
        "subjectId": "5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f",
        "activity": "flashcards",
        "term": "tener (yo)",
        "definition": "tuve",
        "hints": [
          "irregular stem tuv-"
        ],
        "imageUrl": null,
        "queue": "review",
        "due": "2026-01-15T09:30:00.000Z"
      }
    ],
    "answeredCount": 0,
    "finished": false,
    "nextDueAt": null,
    "undoableItemId": null,
    "serverTime": "2026-02-01T12:00:00.000Z"
  }
}
```

## Errors

Every error has the body `{ "error": { "code", "message", "details" } }`.

- `400`: Invalid request body or query
- `401`: Missing, invalid, disabled or expired API key
- `403`: The API key lacks the required scope
- `404`: No practice session with that id exists for this key's user, or it expired. Start a new one with `startPracticeSession`.
- `405`: Method not allowed. Use a method from the Allow header.
- `429`: Rate limit exceeded. Wait for Retry-After before retrying. The rate-limit headers are absent when the key's own limiter rejected the request before the per-endpoint bucket check ran.
- `500`: Internal server error
