Sortd Docs

Classify a thread with AI

Ask Sortd to analyse an email thread (smart status, urgency, sentiment). It's cached-or-enqueue: a cached result returns instantly; otherwise you get a job to poll.

Needs a key with ai.classify. thread_id is the provider (Gmail/Outlook) thread id.

What gets classified (read this first)

Sortd's AI pipeline analyses email that is on a board — threads that have been tasked (and aren't spam/newsletters). For those, this endpoint returns a cached result instantly. For a thread that is not on a board, the queued job will not complete on demand today — it resolves only if the pipeline later analyses that thread. The API says so honestly: the 202 response carries a note to this effect.

So the reliable pattern is: create a task from the thread first (POST /tasks with thread_id), let the pipeline pick it up, then read the analysis here. Don't poll an untasked thread's job in a tight loop expecting it to finish.

1. Request a classification

# curl
curl -X POST "https://api.sortd.com/v2/ai/classify-thread" \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "thread_id": "<thread_id>" }'

If the analysis is cached you get 200 with the result directly. If not, you get 202 Accepted with a job to poll — for a tasked thread typically ready in 10–30s; for an untasked thread it stays queued (see above):

{
  "data": { "status": "queued", "job_id": "job_…", "poll_url": "https://api.sortd.com/v2/ai/jobs/job_…" },
  "meta": { "request_id": "req_…" }
}

2. Poll the job until it's done

Follow poll_url (i.e. GET /ai/jobs/{job_id}) until status is no longer queued/processing.

curl "https://api.sortd.com/v2/ai/jobs/job_…" \
  -H "Authorization: Bearer sk_live_…"

JavaScript — request, then poll if needed

async function classify(threadId) {
  const start = await fetch("https://api.sortd.com/v2/ai/classify-thread", {
    method: "POST",
    headers: { Authorization: "Bearer sk_live_…", "Content-Type": "application/json" },
    body: JSON.stringify({ thread_id: threadId }),
  });
  let body = await start.json();
  if (start.status !== 202) return body.data;          // cached — done

  const jobUrl = "https://api.sortd.com" + body.data.poll_url;
  for (let i = 0; i < 10; i++) {
    await new Promise((r) => setTimeout(r, 3000));      // wait 3s between polls
    const res = await fetch(jobUrl, { headers: { Authorization: "Bearer sk_live_…" } });
    body = await res.json();
    if (body.data.status !== "queued" && body.data.status !== "processing") return body.data;
  }
  throw new Error("classification timed out");
}

Python — request, then poll if needed

import requests, time
H = {"Authorization": "Bearer sk_live_…"}
start = requests.post("https://api.sortd.com/v2/ai/classify-thread",
                      headers=H, json={"thread_id": "<thread_id>"})
body = start.json()
if start.status_code == 202:
    job_url = "https://api.sortd.com" + body["data"]["poll_url"]
    for _ in range(10):
        time.sleep(3)
        body = requests.get(job_url, headers=H).json()
        if body["data"]["status"] not in ("queued", "processing"):
            break
result = body["data"]

3. The result

The finished job (or the cached 200) carries the analysis. You can also re-read cached analyses any time with GET /ai/analyses?thread_id=<thread_id>.

Tip: classification is per-thread and cached, so it's cheap to ask again — don't re-poll a thread you've already classified; read /ai/analyses instead.

If the poll loop times out, the thread almost certainly isn't on a board yet — create a task from it and try again rather than extending the loop.

Next: receive webhooks.