Stop the answer being written
const url = 'http://localhost:8000/api/agents/threads/example/stop/';const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url http://localhost:8000/api/agents/threads/example/stop/ \ --header 'Authorization: Bearer <token>'The coach pressed Stop (#2452).
Closing the ask stream no longer ends an answer: when the coach leaves mid-answer (another conversation, a reload, a closed tab) the server finishes it off the wire and records it whole. So Stop is its own signal: the answer being written ends where it is and is recorded as interrupted, whichever worker is holding it.
The client calls this before it closes its stream, and also when it is showing an answer the server is still finishing (answering: true on the conversation). It closes its stream only once this has answered 204.
A Stop applies to every answer of the conversation that started before it; an answer started afterwards is not affected.
No body. Idempotent, and harmless when nothing is being written: 204 for the caller’s own conversation. Another coach’s id is a 404, never a 403. 503 (stop_unavailable) when the shared cache that carries the signal is down: the answer keeps running, and the client says so rather than showing it cut.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Responses
Section titled “Responses”Stop recorded.
object
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).
Example generated
{ "detail": "example"}The caller’s tier does not include the coaching agents.
object
One of eight canonical strings. The front-end paywall switches copy + CTA on this value (exact string equality — do not localise). quota_exhausted / seat_limit_reached / client_limit_reached / feature_not_in_plan lead to an upgrade CTA; subscription_suspended leads to a Billing Portal CTA so the buyer can update their payment method; exercise_not_in_plan leads to a contact-us CTA — the movement list is sales-managed, no tier adds to it (#2030). trial_expired / access_ended mean the team is read-only (#2901): everything opens, nothing new is created; they lead to a subscribe CTA and always carry reset_at: null.
quota_exhausted- Session quota for the period reached 0subscription_suspended- Stripe dunning in flight (past_due / unpaid)seat_limit_reached- Adding another team member would exceed plan.max_seatsclient_limit_reached- Adding another Client would exceed plan.max_client_profilesfeature_not_in_plan- The tier does not include this whole feature surfaceexercise_not_in_plan- The (activity, exercise) is not in the team’s exercise grantstrial_expired- The team is read-only: its self-serve trial endedaccess_ended- The team is read-only: its paid plan or granted access ended
ISO-8601 UTC timestamp the team’s current period ends. Null when the team has no live subscription (legacy un-migrated row). On Free + paid this is the renewal moment; quota refreshes to exactly plan.session_quota (NOT additive — leftover sessions don’t roll over).
The active plan’s role string (free, starter, professional, organization, strategic). Same value as current_role — kept as two separate fields for forward-compatibility with a future named-plan split where current_plan could carry an SKU and current_role the tier label.
The active plan’s role string. See current_plan.
Which feature surface is locked. Present ONLY on feature_not_in_plan, so one modal can name what’s gated without a detail string per feature (#773). The only value today is agents (aikynetix/agents/services.py::AGENTS_FEATURE).
Examples
Free team opens an agent conversation
{ "detail": "feature_not_in_plan", "reset_at": null, "current_plan": "free", "current_role": "free", "feature": "agents"}object
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).
Example generated
{ "detail": "example"}object
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).
Example generated
{ "detail": "example"}object
503 — a downstream dependency was unavailable and the request could not be served (e.g. enqueue_failed when the analysis queue can’t accept the job). Safe to retry with backoff.
Example generated
{ "detail": "example"}