Skip to content

The card's conversation, or what changed since a cursor

GET
/api/clients/{id}/thread/
curl --request GET \
--url http://localhost:8000/api/clients/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/thread/ \
--header 'Authorization: Bearer <token>'

Without since: the conversation — participants, the newest page of messages (oldest first), the reader’s own marker and settings, and a cursor. With since=<cursor>: what changed since — messages created, edited or deleted, oldest first — with reset: true and the newest page instead when the cursor is stale or a card changed (put it in place of what you hold). Poll it every 8 s with If-None-Match; an unchanged thread is 304 with no body. 400 invalid_cursor.

A coach of the card’s team. Every route answers 404 while client access is switched off on the environment, and 404 client_not_found for a card of another team; a partner API key and a client’s own account get 403. Reading stays open on every plan; posting and editing are 402 on a read-only team or a plan without client access; deleting one’s own message is not. 429 past the account’s rate (chat_read 240/min for reads, chat_post 30/min for writes).

id
required
string format: uuid
lang
string

The cards’ language: en · ru · es.

since
string

The cursor of the last answer: answers what changed since (the delta). Send the last ETag as If-None-Match: 304 when nothing did.

units
string

metric (default) · imperial.

Media typeapplication/json
One of:

GET …/thread/ — the conversation (ClientThread).

object
client_id
required
string format: uuid
cursor
required

Opaque. Send it back as ?since= to poll for what changed.

string
participants
required
Array<object>

ThreadParticipant.

object
id
required
string format: uuid
display_name
required
string
avatar_url
required
string
role
required
  • coach - Coach
  • athlete - Athlete
  • guardian - Parent or guardian
string
Allowed values: coach athlete guardian
active
required

False once this person can no longer read the thread.

boolean
messages
required

The newest page, oldest first.

Array<object>

One message (ThreadMessage).

object
id
required
string format: uuid
author_id
required

The author’s participant id; null once their account is gone.

string format: uuid
nullable
author_role
required
  • coach - Coach
  • athlete - Athlete
  • guardian - Parent or guardian
string
Allowed values: coach athlete guardian
author_name
required

Coach side: a name, else an address. Client side: a name only, never an address — empty when the person gave none.

string
text
required

Empty once deleted, and for a shared result nobody reviewed.

string
reference
required
One of:
One of: discriminator: kind

kind: session — a whole session.

object
placeholder
required

True: kind alone — the card cannot be shown to this reader (or its subject is gone).

boolean
kind
required

The discriminator.

  • session - session
string
Allowed values: session
session_id
string format: uuid
activity
string
title
string
recorded_at
string format: date-time
thumbnail_url
string
nullable
event
required
One of:
  • result_shared - Result shared
  • client_upload - Client sent a clip
  • upload_refused - Client’s clip refused: team out of analyses
  • document_shared - Document shared
string
Allowed values: result_shared client_upload upload_refused document_shared
created_at
required
string format: date-time
edited_at
required
string format: date-time
nullable
deleted
required

Soft delete: the row stays and reads “message deleted”.

boolean
has_older
required
boolean
me_id
required
string format: uuid
last_read_message_id
required
string format: uuid
nullable
can_post
required

Coach side: false while nobody on the client’s side holds access, on a plan without client access, and for staff. Client side: true.

boolean
post_blocked_reason
required
One of:
  • staff - staff
  • plan - plan
  • consent_pending - consent_pending
  • minor - minor
  • invite_pending - invite_pending
  • no_access - no_access
string
Allowed values: staff plan consent_pending minor invite_pending no_access
minor
required
boolean
settings
required

ThreadSettings — the signed-in person’s own, for this conversation.

object
muted
required
boolean
pinned
required
boolean
archived
required
boolean

Examples

The conversation

{
"client_id": "a1f1c0a2-0000-4000-8000-0000000000aa",
"cursor": "41.9b2e4c70d1aa",
"participants": [
{
"id": "c0ac4000-0000-4000-8000-0000000000c1",
"display_name": "Kim Larsen",
"avatar_url": "",
"role": "coach",
"active": true
},
{
"id": "9a7e0000-0000-4000-8000-0000000000b2",
"display_name": "Olga Lebedeva",
"avatar_url": "",
"role": "guardian",
"active": true
}
],
"messages": [
{
"id": "3f2a1b0c-0000-4000-8000-0000000000d3",
"author_id": "c0ac4000-0000-4000-8000-0000000000c1",
"author_role": "coach",
"author_name": "Kim Larsen",
"text": "",
"reference": {
"kind": "session",
"placeholder": false,
"session_id": "5e2b9c41-7d3a-4f80-9b6e-1c0d2e3f4a51",
"activity": "running",
"title": "Treadmill check",
"recorded_at": "2026-11-02T09:40:00Z",
"thumbnail_url": null
},
"event": "result_shared",
"created_at": "2026-11-02T10:16:00Z",
"edited_at": null,
"deleted": false
},
{
"id": "3f2a1b0c-0000-4000-8000-0000000000d3",
"author_id": "c0ac4000-0000-4000-8000-0000000000c1",
"author_role": "coach",
"author_name": "Kim Larsen",
"text": "Nice cadence on this one — let's keep the stride short next week.",
"reference": {
"kind": "session",
"placeholder": false,
"session_id": "5e2b9c41-7d3a-4f80-9b6e-1c0d2e3f4a51",
"activity": "running",
"title": "Treadmill check",
"recorded_at": "2026-11-02T09:40:00Z",
"thumbnail_url": null
},
"event": null,
"created_at": "2026-11-02T10:16:00Z",
"edited_at": null,
"deleted": false
}
],
"has_older": false,
"me_id": "c0ac4000-0000-4000-8000-0000000000c1",
"last_read_message_id": "3f2a1b0c-0000-4000-8000-0000000000d3",
"can_post": true,
"post_blocked_reason": null,
"minor": true,
"settings": {
"muted": false,
"pinned": false,
"archived": false
}
}

Nothing changed since the If-None-Match tag: no body; keep what you hold.

Media typeapplication/json
object
detail
required

Human-readable message, or a stable machine code for the cases a client branches on. The standard envelope for 400 (validation — a field-keyed object may appear instead), 401 (missing / invalid credentials), 403 (authenticated but not permitted), and 404 (absent — cross-team records are collapsed to 404 so the API never leaks the existence of another team’s data).

string

Examples

Example400—ACursorThisRouteNeverGave

400 — a cursor this route never gave

{
"detail": "invalid_cursor"
}
Media typeapplication/json
object
detail
required

Human-readable message, or a stable machine code for the cases a client branches on. The standard envelope for 400 (validation — a field-keyed object may appear instead), 401 (missing / invalid credentials), 403 (authenticated but not permitted), and 404 (absent — cross-team records are collapsed to 404 so the API never leaks the existence of another team’s data).

string

Example generated

{
"detail": "example"
}
Media typeapplication/json
object
detail
required

Human-readable message, or a stable machine code for the cases a client branches on. The standard envelope for 400 (validation — a field-keyed object may appear instead), 401 (missing / invalid credentials), 403 (authenticated but not permitted), and 404 (absent — cross-team records are collapsed to 404 so the API never leaks the existence of another team’s data).

string

Examples

Example403—AClient'sOwnAccount

403 — a client's own account

{
"detail": "client_account"
}
Media typeapplication/json
object
detail
required

Human-readable message, or a stable machine code for the cases a client branches on. The standard envelope for 400 (validation — a field-keyed object may appear instead), 401 (missing / invalid credentials), 403 (authenticated but not permitted), and 404 (absent — cross-team records are collapsed to 404 so the API never leaks the existence of another team’s data).

string

Examples

Example404—NotACardOfTheTeam

404 — not a card of the team

{
"detail": "client_not_found"
}
Media typeapplication/json
object
detail
required

429 — throttled: this account’s (or address’s) rate for the route is used up; try again after wait seconds, which Retry-After repeats. A route with a limit of its own answers its own code instead, documented there.

string
wait

With throttled: whole seconds until the next request can pass.

integer
nullable

Example generated

{
"detail": "example",
"wait": 1
}