Development Profile API

The Development Profile API lets an authenticated user read their own development profile — a periodically regenerated coaching summary derived from the codebook of recurring patterns found in that user’s evaluated sessions. It has two parts: a coach’s note carrying the one insight the data does not state on its own, and a list of skills carrying the current state and direction of each one.

The endpoint is implemented in DevelopmentProfileController and is mounted under /api/development-profile. It operates on the profile of the currently authenticated user (resolved via UserService.currentUser) and therefore requires a valid JWT bearer token (see Authentication). The user id is always taken from the authenticated principal, never from the request, so a consumer can only ever read their own profile.

The profile itself is authored by an LLM tool-loop after a session and stored as a DevelopmentProfile document (one per user per project); this API only reads the latest stored version.

A profile is rebuilt only when the evidence behind it has actually moved, so generatedAt can be older than the user’s last conversation without anything being wrong — an unchanged profile means the latest sessions said nothing new about this person, not that the profile is stale. Poll it after a conversation ends rather than during one; if you cache it, key the cache on generatedAt.

Every string a reader sees is authored in the profile’s own language, including the section headings and the direction labels in labels. The language is whichever language the project’s evaluations are written in, so a client cannot hold a translation table for it. Render labels as given and never substitute your own captions.

The same applies to header.periodSummary: it is a finished sentence because plural forms and date order differ per language and cannot be assembled from a template. Use advisor when you need the period as machine-readable values, and header.periodSummary when you need to show it.

Enumerations (direction, sentiment) are not localised — they stay stable tokens so a client can drive colour, icon and ordering from them, and take the words from labels.

skills[].id is stable across regenerations: the same skill keeps its id for as long as it stays in the profile, so a client can animate a row between two versions, or keep it expanded, by that id. labels is stable too, for as long as language does not change — treat a change of language as the signal to re-render the whole page rather than to diff it.

GET /api/development-profile

Return the current user’s development profile for a given project.

A profile only exists once the project’s codebook has accumulated enough coded occurrences to generate one, so a brand-new user or project will not have one yet. In that case the endpoint returns 200 OK with an empty body rather than an error — the absence of a profile is a normal state, not a failure.

Query Parameters

Parameter Required Type Description

projectId

Yes

UUID

The project whose profile to read. The profile is looked up by the pair (projectId, authenticated user id), so a caller only ever receives their own profile for that project, or nothing.

Takes no path parameters and no request body.

Response

Returns a DevelopmentProfile object (see DevelopmentProfile Object) when one exists, or an empty body (200 OK) when the user has no profile for the project yet.

{
  "advisor": {
    "name": "Jan Novak",
    "periodStart": "2026-06-01",
    "periodEnd": "2026-08-27",
    "sessionsEvaluated": 94,
    "evaluatedDays": 38
  },
  "generatedAt": "2026-09-11T09:10:40.081Z",
  "model": "gpt-5.4",
  "language": "cs",
  "labels": {
    "coachNoteHeading": "Co ti chci říct",
    "skillsHeading": "Tvoje dovednosti",
    "successCriterionLead": "Poznáš to podle:",
    "direction": {
      "Solved": "VYŘEŠENO",
      "Up": "↑ ROSTE",
      "Holding": "= DRŽÍ",
      "Flat": "= STOJÍ",
      "Risk": "↓ RIZIKO"
    }
  },
  "header": {
    "periodSummary": "1. 6. – 27. 8. · 94 hovorů · 38 hodnocených dnů",
    "badge": { "label": "Dvě věci vyřešené, jedna stojí", "sentiment": "Positive" },
    "dataMaturity": null
  },
  "coachNote": {
    "title": "Otázky kladeš líp, ale na špatném místě hovoru",
    "text": "Za tři měsíce jsi udělal dvě věci, které bych nečekal tak rychle ...\n\nDůvod je tenhle: když klient přinese konkrétní detail ...\n\n> Takže příště: až klient řekne něco konkrétního, zůstaň u toho celou další otázku.",
    "successCriterion": "ve třech rozborech po sobě zazní po každém detailu otázka ke stejné lince."
  },
  "skills": [
    {
      "id": "formulace_otazek",
      "direction": "Solved",
      "label": "Formulace otázek",
      "detail": "Sedmnáct hodnocených dnů po sobě proběhlo bez jediného výskytu.",
      "value": null,
      "since": "naposledy 14. 7.",
      "action": null
    },
    {
      "id": "empatie",
      "direction": "Up",
      "label": "Empatie",
      "detail": null,
      "value": "41 → 63 / 100",
      "since": "zlom kolem 20. 7.",
      "action": null
    },
    {
      "id": "otevreni_hovoru",
      "direction": "Holding",
      "label": "Otevření hovoru",
      "detail": "Drží se bez výpadku v každém hovoru.",
      "value": null,
      "since": "38 dnů",
      "action": null
    },
    {
      "id": "hloubka_po_detailu",
      "direction": "Flat",
      "label": "Hloubka po detailu",
      "detail": null,
      "value": "52 → 54 / 100",
      "since": "beze změny od 1. 6.",
      "action": null
    },
    {
      "id": "bezpeci_u_rizika",
      "direction": "Risk",
      "label": "Bezpečí u rizikového výroku",
      "detail": "Po pěti čistých týdnech se to vrátilo třikrát za posledních čtrnáct dnů.",
      "value": null,
      "since": "naposledy včera",
      "action": "uznat závažnost, ověřit význam, zjistit bezpečí — teprve pak dál"
    }
  ]
}

The example above shows every direction value and both the populated and the null case of every optional field. header.dataMaturity is non-null only while the window is still too short to compare periods, for example "1. týden — na srovnání brzy"; coachNote.successCriterion may be absent when the note states no measurable outcome.

Response Objects

DevelopmentProfile Object

Field Type Description

advisor

Object

Machine-readable period facts (see Advisor Object) — computed by the service, never authored.

generatedAt

Instant

Timestamp of the run that last produced this profile content.

model

String

Identifier of the LLM that generated the profile.

language

String

Language tag of every authored string, as reported by the model (e.g. cs). Changes only when the project’s evaluations change language.

labels

Object

The UI strings for that language (see Labels Object). Stable across regenerations.

header

Object

Period line and trajectory badge (see Header Object).

coachNote

Object

The coach’s note (see CoachNote Object).

skills

Array

2–8 skill rows (see Skill Object), in the order they should be rendered.

Advisor Object

Field Type Description

name

String

Display name of the coached user.

periodStart

String

ISO date of the first coded occurrence in the window.

periodEnd

String

ISO date of the last one.

sessionsEvaluated

Integer

Distinct sessions behind the profile.

evaluatedDays

Integer

Distinct calendar days on which the user was evaluated.

Labels Object

Field Type Description

coachNoteHeading

String

Heading to render above coachNote.

skillsHeading

String

Heading to render above skills.

successCriterionLead

String

Lead-in to render before coachNote.successCriterion.

direction

Object

Display text per direction token; contains at least every token used in skills.

Header Object

Field Type Description

periodSummary

String

Finished, localised period sentence — render as given.

badge

Object

Trajectory badge (see Badge Object).

dataMaturity

String or null

Present only while the window is too short to compare periods.

Badge Object

Field Type Description

label

String

Short present-tense label naming the trajectory.

sentiment

String

One of Positive, Watch, Risk, Neutral. Drives colour, not wording.

CoachNote Object

Field Type Description

title

String

The point of the note in one line. Plain text.

text

String

The note itself, in Markdown — see below.

successCriterion

String or null

How the reader will know it worked.

text is the only Markdown field in the profile; everything else, title included, is plain text. It holds 2–3 prose paragraphs separated by a blank line, and always ends with what to do in the next conversation as a blockquote (> …). title and text together are at most 1400 characters. Render it through a Markdown renderer: the paragraph breaks and the closing quote are part of how the note reads, and a client that prints the raw string shows the reader a stray >.

Skill Object

Field Type Description

id

String

Stable snake_case key; the same skill keeps its id across regenerations.

direction

String

One of Solved, Up, Holding, Flat, Risk. Drives colour and the risk treatment.

label

String

Short skill name.

detail

String or null

One whole sentence of context, capitalised and closed with a period. The only row field written as a sentence — value, since and action are bare fragments.

value

String or null

A figure worth showing, already formatted, e.g. 41 → 63 / 100.

since

String or null

When it last moved or was last seen.

action

String or null

What to do about it. Always present for Risk, always null otherwise.