> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.alfa.boosted.ai/alfa-preview/threads/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.alfa.boosted.ai/_mcp/server. # Threads > **Warning** > > This API is currently in Preview. ## Overview Threads keep every Alfa API exchange stateful so you can execute long-running research, pause work, and resume with full context. Each thread stores metadata, message history, task progress, and structured artifacts generated during analysis. * Use `POST /v2/threads` to create a new thread and `GET /v2/threads` to discover existing ones. * Send instructions with `POST /v2/threads/{thread_id}/messages` and stream events using [gRPC or SSE streaming](/alfa-preview/streaming). * See chat history and structured data table outputs with `GET /v2/threads/{thread_id}/messages` and `GET /v2/threads/{thread_id}/artifacts/{artifact_id}` respectively. > **Tip** > > Review the [API reference](/api-reference#tag/thread) after finishing this guide to inspect every request and response schema. ## Thread lifecycle ### Create or reuse a thread Call `POST /v2/threads` with a descriptive `thread_name`. ### Send a message and stream updates Submit the user prompt with `POST /v2/threads/{thread_id}/messages`. Use [gRPC or SSE streaming](/alfa-preview/streaming) to receive incremental reasoning, artifacts, and completion events in real time. ### Collect outputs Outputs are streamed through the streaming endpoints, but you can fetch the latest complete messages using `GET /v2/threads/{thread_id}/messages?start_index=0&limit_num=25`. Structured data artifacts are accessible via `GET /v2/threads/{thread_id}/artifacts/{artifact_id}`. ## Create and manage threads Threads are lightweight resources. You can list, rename, or delete them without touching other workloads. **`threads.py`** ```python title="threads.py" import os import requests BASE_URL = os.getenv("ALFA_API_BASE_URL", "https://sandbox.api.boosted.ai") ACCESS_TOKEN = os.environ["ALFA_ACCESS_TOKEN"] def auth_headers() -> dict: return {"Authorization": f"Bearer {ACCESS_TOKEN}", "Content-Type": "application/json"} def create_thread(thread_name: str) -> str: resp = requests.post( f"{BASE_URL}/v2/threads", headers=auth_headers(), json={"thread_name": thread_name}, timeout=30, ) resp.raise_for_status() return resp.json()["thread_id"] def list_threads() -> list[dict]: resp = requests.get(f"{BASE_URL}/v2/threads", headers=auth_headers(), timeout=30) resp.raise_for_status() return resp.json()["threads"] def rename_thread(thread_id: str, new_name: str) -> None: payload = {"name": new_name} resp = requests.patch( f"{BASE_URL}/v2/threads/{thread_id}", headers=auth_headers(), json=payload, timeout=30, ) resp.raise_for_status() ``` **`delete-thread.mjs`** ```javascript title="delete-thread.mjs" import fetch from "node-fetch"; const BASE_URL = process.env.ALFA_API_BASE_URL ?? "https://sandbox.api.boosted.ai"; const HEADERS = { "Authorization": `Bearer ${process.env.ALFA_ACCESS_TOKEN}`, "Content-Type": "application/json", }; export async function deleteThread(threadId) { const resp = await fetch(`${BASE_URL}/v2/threads/${threadId}`, { method: "DELETE", headers: HEADERS, }); if (!resp.ok) { throw new Error(`Failed to delete thread ${threadId}: ${resp.statusText}`); } } ``` > **Note** > > Deleting a thread removes all artifacts and history permanently. Be careful when issuing `DELETE /v2/threads/{thread_id}`. ## Send messages `POST /v2/threads/{thread_id}/messages` validates the caller has access to the thread, queues the task, and returns immediately. To receive real-time updates, use [gRPC or SSE streaming](/alfa-preview/streaming). **`send_message.py`** ```python title="send_message.py" import os import requests BASE_URL = os.getenv("ALFA_API_BASE_URL", "https://sandbox.api.boosted.ai") ACCESS_TOKEN = os.environ["ALFA_ACCESS_TOKEN"] def auth_headers(): return {"Authorization": f"Bearer {ACCESS_TOKEN}"} def send_message(thread_id: str, message: str) -> dict: resp = requests.post( f"{BASE_URL}/v2/threads/{thread_id}/messages", headers=auth_headers(), data={"message": message}, timeout=30, ) resp.raise_for_status() return resp.json() ``` > **Tip** > > To stream thread events in real time, see the [streaming guide](/alfa-preview/streaming) for both gRPC and SSE options. If a stream closes before completion, call `GET /v2/threads/{thread_id}/progress` to check status. ## Retrieve history and artifacts Use the history endpoint to build up the log of chat messages sent over the course of a conversation, and the artifacts endpoint to retrieve structured data output. > **Info** > > **Artifacts** are structured data outputs (such as tables, charts, or JSON objects) generated during thread execution. Unlike plain text responses, artifacts contain formatted data that can be retrieved and rendered separately using their unique `artifact_id`. **`history_and_artifacts.py`** ```python title="history_and_artifacts.py" import os import requests BASE_URL = os.getenv("ALFA_API_BASE_URL", "https://sandbox.api.boosted.ai") ACCESS_TOKEN = os.environ["ALFA_ACCESS_TOKEN"] def auth_headers(): return {"Authorization": f"Bearer {ACCESS_TOKEN}", "Content-Type": "application/json"} def get_history(thread_id: str, *, start_index: int = 0, limit_num: int = 50) -> list[dict]: params = {"start_index": start_index, "limit_num": limit_num} resp = requests.get( f"{BASE_URL}/v2/threads/{thread_id}/messages", headers=auth_headers(), params=params, timeout=30, ) resp.raise_for_status() return resp.json()["messages"] def get_artifact(thread_id: str, artifact_id: str) -> dict: resp = requests.get( f"{BASE_URL}/v2/threads/{thread_id}/artifacts/{artifact_id}", headers=auth_headers(), timeout=30, ) resp.raise_for_status() return resp.json()["artifact"] ``` Tie artifact IDs to the `message_id` returned by `GET /v2/threads/{thread_id}/messages` so you can render both the prose answer and the structured data. ## Citations Alfa responses include **citations** that link claims in the generated text back to their source material. Citations appear in three places: * **Messages** — each message returned by `GET /v2/threads/{thread_id}/messages` includes a `citations` array with source metadata. * **Streaming events** — both [SSE and gRPC streams](/alfa-preview/streaming) deliver citations alongside message chunks and final messages as they arrive. * **Artifacts** — structured data outputs (tables, charts) can carry their own citation references tied to individual data points. Each citation carries a `citation_type` that identifies the source category (such as `web`, `news_development`, or `document`) along with type-specific fields like URLs, snippet positions, and article counts. Use this metadata to build source attribution UI — for example, rendering footnotes, expandable source cards, or inline reference links for end users. > **Info** > > For the full list of citation types, field schemas, and JSON examples, see the [streaming guide](/alfa-preview/streaming) which covers both SSE and gRPC citation formats. ## Upload user files to a thread Attach supporting documents directly to a thread so the model can cite them during follow-up prompts. The endpoint expects multipart form data and associates the uploaded blob with the specified thread. **`upload_file.py`** ```python title="upload_file.py" import os import requests BASE_URL = os.getenv("ALFA_API_BASE_URL", "https://sandbox.api.boosted.ai") ACCESS_TOKEN = os.environ["ALFA_ACCESS_TOKEN"] def upload_user_file(thread_id: str, file_path: str) -> dict: with open(file_path, "rb") as fh: files = {"file": fh} resp = requests.post( f"{BASE_URL}/v2/threads/{thread_id}/files", headers={"Authorization": f"Bearer {ACCESS_TOKEN}"}, files=files, timeout=60, ) resp.raise_for_status() return resp.json() response = upload_user_file(thread_id, "documents/q2.pdf") print("Uploaded file:", response["file_id"]) ``` > **Tip** > > Store the returned `file_id` next to the thread metadata so later `POST /v2/threads/{thread_id}/messages` calls can reference the uploaded evidence in user prompts. ## Cancel a running task Use the cancellation endpoint to stop the currently processing message whenever a user changes their mind. The API records a "User cancelled" entry in the thread so downstream reviewers can see why work halted. **`cancel.py`** ```python title="cancel.py" def cancel_thread_task(thread_id: str) -> None: resp = requests.delete( f"{BASE_URL}/v2/threads/{thread_id}/task", headers=auth_headers(), timeout=15, ) resp.raise_for_status() cancel_thread_task(thread_id) ``` > Learn how to orchestrate conversational threads with the Alfa (Preview) API.