User History APIs

Save every question a signed-in reader asks, let them reopen past conversations, and notify them when Miso has a fresher answer.

Records are created for you

You do not write history. When a signed-in user asks a question through POST /v1/ask/questions, Miso records it in the background. Only the first question of a conversation becomes a history row (a thread); follow-ups attach to that thread and move it to the top of the list. Questions from anonymous users are never saved.

Pass log_user_history: false on POST /v1/ask/questions to keep one question out of history — useful for a per-question "private mode" toggle.

Authentication

Every endpoint needs your API key and an authenticated user:

  • Secret key (server side) — pass user_id in the body; it is trusted.
  • Publishable key (browser) — must carry a signed JWT in Authorization: Bearer <token> or a jwt_token body field. Sign it with HS256 using your app's secret key; its user_id (or sub) claim is the authenticated user.

If user_id appears in both the JWT and the body, the two must match or the request returns 401.

Answer Updates

A reader can subscribe to a thread. Miso watches that topic and, when it finds important new content, appends a fresh follow-up answer and raises an unread indicator. Miso judges significance before appending, so no update means no indicator.

Two levels are reported, both derived at read time by comparing the last update with the last dismissal:

  • Account level — does this user have any unseen update? Poll POST /v1/ask/user_history/thread/updates.
  • Thread level — does this thread have one? The has_new field on the history list.

Typical front-end flow

  1. Poll .../thread/updates for the account indicator, and call /v1/ask/user_history for the sidebar.
  2. Open a thread with .../thread, fetch every answer in one call with /v1/ask/answers, then clear it with .../dismiss_thread.
  3. Offer .../subscribe on threads the reader cares about.

Examples

The examples below build one sidebar: list conversations, open one, then offer updates. Each request needs your API key plus an authenticated user (see Authentication above).

Example 1 — Render the sidebar (simplest)

POST /v1/ask/user_history
{ "user_id": "alice", "rows": 20 }
{
  "message": "success",
  "data": [
    {
      "id": "3xK7mN",
      "time": "2026-06-12T10:15:00Z",
      "question_id": "0e1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4…",
      "question": "How do interest rate cuts affect mor…",
      "subscribed": true,
      "has_new": true
    }
  ]
}

Render question as the row label. Show the thread-level unread indicator when has_new is true.

Example 2 — Open a thread in two calls

Get the conversation's questions in order:

POST /v1/ask/user_history/thread
{
  "question_id": "0e1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
  "user_id": "alice"
}
{
  "message": "success",
  "data": {
    "question_ids": [
      "0e1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
      "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
    ],
    "has_more": false
  }
}

Then fetch every answer in one call:

POST /v1/ask/answers
{
  "question_ids": [
    "0e1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
    "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
  ]
}

data is aligned with your input, and an id you cannot see comes back as null. Use this instead of one poll per question — it is one round trip rather than many.

To page a long thread, pass the last question_id as after and repeat while has_more is true. Pass "order": "desc" for a newest-first view.

Example 3 — Let the reader manage the list

POST /v1/ask/user_history/thread/rename
{
  "question_id": "0e1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
  "user_id": "alice",
  "title": "Mortgage research"
}

Delete selected rows with the opaque id values from Example 1 — not the question_id:

POST /v1/ask/user_history/delete
{ "ids": ["3xK7mN", "9pQ2rS"], "user_id": "alice" }

Example 4 — Offer Answer Updates

Subscribe the thread the reader cares about:

POST /v1/ask/user_history/thread/updates/subscribe
{
  "user_id": "alice",
  "thread_id": "0e1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b"
}

Poll the account-level indicator, for example every 60 seconds:

POST /v1/ask/user_history/thread/updates
{ "user_id": "alice" }
{ "message": "success", "data": { "has_new": true } }

When it turns true, refresh the sidebar. The row whose has_new is true holds the new answer. Reopen that thread to read it.

Example 5 — Clear the indicators correctly

The two calls do different jobs:

POST /v1/ask/user_history/thread/updates/dismiss_thread
{
  "user_id": "alice",
  "thread_id": "0e1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b"
}

Call this when the reader opens a thread. It clears that thread, and clears the account level too if no other thread is waiting.

POST /v1/ask/user_history/thread/updates/dismiss_overall
{ "user_id": "alice" }

Call this when the reader dismisses the notification without opening anything. Every thread keeps its own indicator, so nothing is lost.

Example 6 — Do it all from the browser

A publishable key must carry a JWT. Sign it on your server with your secret key (HS256) and put user_id in the claims. Then send it with every call:

POST /v1/ask/user_history
Authorization: Bearer <signed-jwt>
{ "rows": 20 }

Omit user_id from the body — it comes from the token. If you send both, they must match, or the request returns 401.