API

The REST API is the whole surface: captures in, patterns out, with correlations, flows, review and a timeline in between. This page explains the concepts and the lifecycle; the generated reference on the API domain has every field.

Basics

Every route lives on the API domain, takes and answers JSON, and wants a bearer token. Answers wrap the resource in data; lists add links and meta with the page and the total.

GET    /captures              GET    /patterns
POST   /captures              GET    /patterns/{id}
GET    /captures/{id}         PATCH  /patterns/{id}
DELETE /captures/{id}         PATCH  /correlations/{id}
GET    /timeline?from&to      GET    /settings/me
GET    /users/me              PUT    /settings/me/{key}
GET /captures · a list
{
  "data": [ … ],
  "links": {"first": "…", "last": "…", "prev": null, "next": "…"},
  "meta": {"current_page": 1, "per_page": 15, "total": 42, …}
}

The generated reference has every route, field and error, read off the code, so it is always current. This page tells you what the resources mean. /docs/api, /openapi.json.

What happens to a capture

One capture, one run, fixed order. Each step is a call to a language model except detection, which is counting.

StepWhat it does
TranslateFills translated and language. English in is English out.
SegmentCuts the text into situations, verbatim, never rewritten.
ExtractPer segment: feelings, then triggers and behaviours, then persons, locations and activities.
CategoriseGroups a new trigger or behaviour with the category that means the same, or makes one.
DetectCounts categories and the feelings they come with across days, and opens or updates patterns.
CorrelateCounts which persons, locations and activities each pattern happens more with.
FlowsLinks trigger, feeling and behaviour inside a segment for each pattern it belongs to.
DescribeWrites each new pattern's description in your language.

Count on thirty seconds to two minutes. Detection also re-runs every night at your local midnight, so patterns can appear, change or close without a new capture.

Captures

A capture is the text you handed in. Fields: original, exactly as posted; translated, always English; language, the ISO code found; origin, text for now; occurred_at, the moment it is about; status; and segments once processed.

Status runs queued, processing, processed, failed. A failed capture keeps its text and nothing else; post it again. Deleting a capture removes what was found in it and re-runs detection, so a pattern that only lived in that capture closes.

Lists are paginated with page and limit, newest first.

Segments and tags

A segment is a verbatim slice of the translated text: one thing that happened with the feeling it gave and what you did. Each carries tags of a type: trigger, positive_feeling, negative_feeling, good_behaviour, bad_behaviour, person, location, activity. Triggers and behaviours also name their category.

Patterns

A pattern is a category that keeps coming back with the same feelings: something that keeps setting you off, or something you keep doing. It needs at least three different days. Fields:

  • name — the category, like work contact or procrastination.
  • sentiment — positive or negative. A good behaviour makes a positive pattern, a bad one a negative pattern, a trigger can make either.
  • impact — low, moderate or high: how often it shows up, weighed against how much you write.
  • urgent — true when it showed up on three different days in the last week.
  • feelings — up to four feelings it comes with.
  • description — a few sentences written for you, in your language.
  • status — detected, accepted or rejected.
  • captures_count, last_detected_at, closed_at — how much it rests on, when it was last seen, and when it stopped showing up.

A pattern that goes quiet for a week closes and reopens when it returns; the same pattern, not a new one. Reading one pattern adds its captures as summaries, its correlations and its flows.

GET /patterns/{id}
{
  "data": {
    "id": "7c1e…",
    "name": "work contact",
    "sentiment": "negative",
    "impact": "low",
    "urgent": true,
    "status": "detected",
    "description": "Contact from work, a call or a message about a deadline, keeps landing as a knot in your stomach. …",
    "feelings": [{"id": "…", "name": "anxious"}],
    "captures_count": 4,
    "last_detected_at": "2026-09-26T22:00:03.000000Z",
    "closed_at": null,
    "created_at": "2026-09-24T22:00:01.000000Z",
    "captures": [{"id": "9d2f…", "status": "processed", "occurred_at": "2026-09-26T16:10:00.000000Z"}, …],
    "correlations": [
      {"id": "…", "tag": {"id": "…", "type": "person", "name": "boss"}, "impact": "moderate", "amount": 3, "status": "detected"}
    ],
    "flows": [
      {"id": "…", "segment_id": "b41c…", "trigger": "boss calling about the report", "feeling": "anxious", "behaviour": "scrolling instead of working"}
    ]
  }
}

Correlations

A correlation is a person, location or activity a pattern happens more with. It carries the tag, an impact, the amount of times they were seen together, and a status. The list is the top few per type, refreshed on every run, so one that drops out of the top disappears unless you accepted it.

Flows

A flow is one segment told in order: trigger, feeling, behaviour. What happened, what you felt, what you did. Flows are only linked, never invented: each of the three is a tag that was extracted from that segment.

Timeline

The timeline is one entry per calendar day in your timezone, empty days included, between from and to, at most 92 days apart. Each day lists its captures as id, status and occurred_at, and the patterns those captures belong to.

GET /timeline
curl "https://api.undrstand.com/timeline?from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer $TOKEN" -H "Accept: application/json"

# {"data": [
#   {"date": "2026-09-01", "captures": [], "patterns": []},
#   …
#   {"date": "2026-09-26",
#    "captures": [{"id": "9d2f…", "status": "processed", "occurred_at": "…"}],
#    "patterns": [{"id": "7c1e…", "name": "work contact", "sentiment": "negative", "impact": "low"}]}
# ]}

Review

You can tell undrstand a pattern or a correlation is yours or is not. Accepting keeps it listed and off the nightly chopping block. Rejecting a pattern closes it, drops its flows and correlations, and teaches the extraction to leave that topic alone in everything you hand in after. A rejected pattern is still readable by id, never listed.

PATCH /patterns/{id}
curl -X PATCH https://api.undrstand.com/patterns/7c1e… \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -H "Accept: application/json" \
  -d '{"status": "accepted"}'      # or "rejected"; same shape on /correlations/{id}

Only review on the user's word. An app that rejects on its own initiative is teaching the analysis to ignore things the user never saw.

Settings

Two settings matter here: timezone, which cuts days and runs the nightly refresh at your local midnight, and app_locale, which sets the language of pattern descriptions. Read them all, or write one by key.

GET https://api.undrstand.com/settings/me
# {"data": [{"key": "timezone", "value": "Europe/Amsterdam"}, {"key": "app_locale", "value": "nl"}, …]}

PUT https://api.undrstand.com/settings/me/app_locale   {"value": "nl"}

Errors

CodeWhen
401No token, an expired token, or a revoked one.
403The token lacks the scope; the message names it.
404The record does not exist or belongs to someone else. The two are not told apart.
422Validation failed: message plus errors keyed by field, each a list of sentences.
429Too many requests on the route.
HTTP/1.1 422 Unprocessable Content
{
  "message": "The to field must be a date after or equal to from.",
  "errors": {"to": ["The to field must be a date after or equal to from."]}
}

Where to next

  • /docs/api — The generated reference on the API domain, with every field.
  • Apps and OAuth — Apps and OAuth, for a frontend other people sign in to.