Get API key

Conversations

Read, generate, and delete a user's stored conversations.

The /v1/conversations/* endpoints expose a user's saved chat history, the same conversations the PrivateMind app sidebar shows. Use them to list, fetch, stream-generate, and delete on behalf of a signed-in user.

Server-side decryption and source redaction are handled automatically.

Authentication: exchanging an application key for a user key

Before calling any /v1/conversations/* or /v1/sources/* endpoint, swap your application key for a user-scoped bearer:

POST /v1/auth/exchange

Request body

JSON
{
  "api_key": "pmind-..."
}
Field Type Notes
api_key string A valid application or admin key.

Response

JSON
{
  "success": true,
  "user_key": "pminduser_abc123...",
  "expires_at": "2026-06-04T16:00:00.000Z"
}
Field Type Notes
user_key string Short-lived bearer to use on user-scoped routes.
expires_at string (ISO 8601) When the key becomes invalid. Typically one hour.
cURL
curl -s "https://api.privatemind.com/v1/auth/exchange" \
  -H "Content-Type: application/json" \
  -d '{"api_key": "pmind_your_application_key"}'

List conversations

GET /v1/conversations returns every non-archived conversation owned by (or shared with) the calling user, newest first. Each item carries its most recent message so a sidebar render needs no follow-up call.

Query parameters

Param Type Notes
project_id string Filter to a project. Pass none to match conversations with no project assigned. Omit for all.
cloud_agents "true" If set, returns only cloud-agent conversations. Default excludes them.
archived "true" If set, returns only archived conversations (the "Archived" view). Default returns non-archived only.

Response

JSON
{
  "success": true,
  "body": [
    {
      "id": 1234,
      "user_id": 42,
      "title": "Refactor the billing service",
      "project_id": null,
      "cloud_agent_task_id": null,
      "archived_at": null,
      "last_message_at": "2026-05-26T14:21:08.123Z",
      "source_count": 2,
      "messages": [
        {
          "message": "Here's a sketch of the new structure...",
          "role": "assistant",
          "created_at": "2026-05-26T14:21:08.123Z",
          "conversation_id": 1234
        }
      ]
    }
  ],
  "total": { "types": ["chat", "agent"] }
}

messages always contains a single entry: the latest message in the conversation. The full transcript is fetched via GET /v1/conversations/{id}.

cURL
curl -s "https://api.privatemind.com/v1/conversations" \
  -H "Authorization: Bearer $PMIND_USER_KEY"

Get one conversation

GET /v1/conversations/{id} returns the full transcript, sources, and participants for a single conversation. The user must own or be a participant in the conversation; otherwise 404.

The branch the user has navigated to (sibling messages, regenerations) is resolved server-side. messages is the linear visible thread, with sibling_ids on each message so a UI can render branch switchers.

Response

JSON
{
  "success": true,
  "conversation": {
    "id": 1234,
    "user_id": 42,
    "title": "Refactor the billing service",
    "project_id": null,
    "archived_at": null,
    "last_message_at": "2026-05-26T14:21:08.123Z",
    "sources": [
      {
        "id": 88,
        "source_type": "vectorized_file",
        "source_name": "billing-spec.pdf",
        "source_config": { },
        "is_active": true,
        "use_in_tasks": true,
        "handler": "vectorizedFileHandler",
        "source_description": null,
        "is_owner": true
      }
    ],
    "participants": [
      {
        "user_id": 42,
        "role": "owner",
        "first_name": "Ada",
        "last_name": "Lovelace",
        "email": "ada@example.com",
        "joined_at": "2026-05-20T09:00:00.000Z"
      }
    ],
    "messages": [
      {
        "id": 9001,
        "conversation_id": 1234,
        "user_id": 42,
        "message": "How should we split the invoicing module?",
        "reasoning_content": null,
        "role": "user",
        "created_at": "2026-05-26T14:20:42.000Z",
        "is_read": true,
        "attachments": [],
        "model_name": null,
        "parent_message_id": null,
        "active_sibling_message": true,
        "sibling_ids": [9001],
        "metadata": {}
      },
      {
        "id": 9002,
        "conversation_id": 1234,
        "user_id": 42,
        "message": "Here's a sketch of the new structure...",
        "reasoning_content": null,
        "role": "assistant",
        "created_at": "2026-05-26T14:21:08.123Z",
        "is_read": true,
        "attachments": [],
        "model_name": "kimi-k2-6",
        "parent_message_id": 9001,
        "active_sibling_message": true,
        "sibling_ids": [9002],
        "metadata": {}
      }
    ]
  }
}

source_config is redacted server-side based on is_owner: non-owners see the source listing but not its private connection fields.

cURL
curl -s "https://api.privatemind.com/v1/conversations/1234" \
  -H "Authorization: Bearer $PMIND_USER_KEY"

List sources attached to a conversation

GET /v1/conversations/{id}/sources returns the active sources currently attached to a conversation: vectorized files, tabular files, source-library entries, remote DBs, and so on.

Response

JSON
{
  "success": true,
  "body": [
    {
      "id": 88,
      "source_type": "vectorized_file",
      "source_name": "billing-spec.pdf",
      "source_config": { },
      "handler": "vectorizedFileHandler",
      "source_description": null,
      "is_owner": true
    }
  ],
  "total": 1
}

Only active, non-deleted source attachments are returned. The full source's source_config follows the same owner-based redaction as the embedded sources on GET /v1/conversations/{id}.

cURL
curl -s "https://api.privatemind.com/v1/conversations/1234/sources" \
  -H "Authorization: Bearer $PMIND_USER_KEY"

Generate an assistant reply

POST /v1/conversations/{id}/generate streams an assistant reply for a previously-saved user message. The response is PrivateMind-shape SSE, not OpenAI deltas. The server pipes upstream chunks through verbatim, including the data: [DONE] terminator.

Prerequisites

To call this endpoint you need a saved message row in the conversation. Create one first by sending a user message through the chat widget or app UI. (Programmatic message creation is on the roadmap but not yet exposed.)

Query parameters

Param Type Notes
model string Model id to generate with (e.g. kimi-k2-6).
thinking "true" / "false" Default true. Set false to disable thinking on hybrid models.
webSearch "true" / "false" Default false. Honoured only if the org has web search enabled.

Request body

JSON
{
  "conversation_id": 1234,
  "message_id": 9001
}

message_id must reference a message in the conversation owned by the calling user.

Response

Content-Type: text/event-stream. Frames look like:

Text
data: {"conversation_id": 1234, "title": "New Chat"}

data: {"chunk": "Here's"}

data: {"chunk": " a sketch..."}

data: [DONE]

The first frame carries the conversation id and current title. Subsequent frames carry model output and any agentic events. On agentic runs (sources attached, shell tools enabled, web search) the frames also include tool-call events.

cURL
curl -N "https://api.privatemind.com/v1/conversations/1234/generate?model=kimi-k2-6" \
  -H "Authorization: Bearer $PMIND_USER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"conversation_id": 1234, "message_id": 9001}'

-N disables curl's output buffering so SSE frames print as they arrive.

Delete conversations

POST /v1/conversations/delete batch-deletes conversations the calling user owns (or admins on a shared conversation). Deletion is permanent: messages, attachments, vectorized file records, conversation-source maps, participants, and project links are all removed in one transaction.

Request body

JSON
{
  "conversation_ids": [1234, 1235]
}
Field Type Notes
conversation_ids number[] One or more conversation ids. Each must be owned by the user, or the user must hold owner / admin participant role on it.

Response

JSON
{
  "success": true,
  "message": "Conversations deleted successfully"
}

If any id in the batch isn't owned (or share-admin'd) by the user, the whole batch returns 404 and nothing is deleted.

cURL
curl -s "https://api.privatemind.com/v1/conversations/delete" \
  -H "Authorization: Bearer $PMIND_USER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"conversation_ids": [1234, 1235]}'

Errors

These routes return the standard HTTP codes documented on the Errors page, with a few specifics:

Code Meaning
400 Malformed conversation id (path segment must be alphanumeric, -, _, 1–64 chars), or invalid request body.
403 user_key_required: the bearer is an admin or application key. Mint a user key via POST /v1/auth/exchange.
404 Conversation not owned by the user, not shared with the user, or doesn't exist. The same code covers "message not found in conversation" on generate.
429 Per-user-key rate limit.
502 Upstream backend unreachable or returned a 5xx.

Where next

  • Authentication: the standard bearer shape used by /v1/chat/completions and friends.
  • Chat completions: the stateless OpenAI-shape inference endpoint. Use it when you don't need to persist or share the conversation.
  • Streaming: how OpenAI-shape SSE works on /v1/chat/completions. The PM-shape stream on /generate is different but the connection-handling advice carries over.
  • Errors: retry strategy and the error envelope.