Skip to content

Edit one's own message

PATCH
/api/portal/{client_id}/thread/messages/{message_id}/
curl --request PATCH \
--url http://localhost:8000/api/portal/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/thread/messages/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "text": "Keep the stride short." }'

The author only (403 not_author), within 24 hours of posting (409 edit_window_closed); not a deleted message (409 message_deleted) nor one the server posted (409 message_not_editable); 409 thread_archived once a team move closed the conversation.

The signed-in person’s grant on the card in the path — View is enough. 404 while client access is switched off; 404 card_not_found for a card the account never held; 403 access_ended (reason: revoked · team_plan · minor) and consent_required as on every portal route; a partner API key gets 403. 429 past the account’s rate (chat_read 240/min for reads, chat_post 30/min for writes).

client_id
required
string format: uuid
message_id
required
string format: uuid

PATCH …/thread/messages/<message_id>/.

object
text

Plain text, at most 4000 characters; links become clickable on render, nothing else is interpreted.

string
<= 4000 characters

Examples

ExampleNewWords

New words

{
"text": "Keep the stride short."
}
Media typeapplication/json

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

Examples

ExampleEdited
{
"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": "2026-11-02T10:20:00Z",
"deleted": false
}
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

Example generated

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

access_ended, consent_required, or DRF’s message for a partner API key.

string
reason

With access_ended: why the card no longer opens — revoked, team_plan or minor.

string
client_name
string
team_name
string
consent_version

With consent_required: the version to accept.

string
kinds

With consent_required: the texts to show.

Array<string>

Examples

403 — someone else's

{
"detail": "not_author"
}
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

404 — not a message of this card

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

409 — the request conflicts with current state. detail is a stable code (e.g. reanalyze_in_flight, already_on_plan, email_in_use). Some 409s carry extra context fields alongside detail; those are documented on the specific endpoint that emits them.

string

Examples

Example409—TooLate

409 — too late

{
"detail": "edit_window_closed"
}
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
}