API reference

Conversations

Past Research Companion conversations: read them with their messages and attached files, rename or delete them. Nothing can be sent through the API.

Paths are relative to https://kleiobase.com/api/v1.

get/workspaces/{workspace_id}/conversations

List conversations

Newest first by updated_at, as the app lists them. A conversation that gets a new message or a new title moves to the front, so it can appear again on a later page.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
limitquerynoPage size. Default 25, max 100.
cursorquerynoThe next_cursor of the previous page. Opaque.

Responses

  • 200 A page of conversations. (ConversationList)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan. (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. (ErrorResponse)
  • 422 Invalid limit or cursor. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
200 response schema
{
  "type": "object",
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "title": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string",
            "description": "Changes when a message is added or the conversation is renamed."
          },
          "message_count": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991,
            "description": "Messages the user sees in the conversation (their own and the companion's replies)."
          }
        },
        "required": [
          "id",
          "title",
          "created_at",
          "updated_at",
          "message_count"
        ]
      }
    },
    "next_cursor": {
      "description": "Pass as ?cursor to get the next page. Null on the last page.",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "data",
    "next_cursor"
  ]
}
get/workspaces/{workspace_id}/conversations/{conversation_id}

Get a conversation with its messages

One page of messages, oldest first (limit and cursor page the messages). Messages are as the user saw them: what they sent and the companion's replies, the people and records each reply cites, and the files and knowledge base items each message carried. The companion's internal working (its reasoning, tool calls, summaries) is not included.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
conversation_idpathyesThe Research Companion conversation id.
limitquerynoPage size. Default 25, max 100.
cursorquerynoThe next_cursor of the previous page. Opaque.

Responses

  • 200 The conversation and a page of messages. (ConversationDetail)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan. (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. Also: the conversation is not in this workspace. (ErrorResponse)
  • 422 Invalid limit or cursor. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
200 response schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "title": {
      "type": "string"
    },
    "created_at": {
      "type": "string"
    },
    "updated_at": {
      "type": "string",
      "description": "Changes when a message is added or the conversation is renamed."
    },
    "message_count": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991,
      "description": "Messages the user sees in the conversation (their own and the companion's replies)."
    },
    "messages": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "role": {
            "type": "string",
            "enum": [
              "user",
              "assistant"
            ],
            "description": "user: what the user sent. assistant: the companion's reply."
          },
          "text": {
            "type": "string",
            "description": "The message as the user saw it, in Markdown. In replies, people and records are cited as links such as [Person: Name](/knowledge-base/people/{id}); `citations` lists their ids."
          },
          "citations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "person",
                    "record"
                  ]
                },
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                }
              },
              "required": [
                "type",
                "id"
              ]
            },
            "description": "People and records cited in a reply, each once. Empty for user messages."
          },
          "attachments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "file_id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                  "description": "Pass to GET .../conversations/{conversation_id}/files/{file_id} for a download URL."
                },
                "filename": {
                  "type": "string"
                },
                "mime_type": {
                  "type": "string",
                  "description": "image/jpeg, image/png, image/webp or application/pdf."
                }
              },
              "required": [
                "file_id",
                "filename",
                "mime_type"
              ]
            },
            "description": "Files the user attached to this message."
          },
          "attached_items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "record",
                    "person",
                    "match",
                    "conflict",
                    "finding"
                  ]
                },
                "id": {
                  "type": "string",
                  "description": "The item's id, for GET .../records/{id}, .../people/{id}, .../matches or .../findings/{id}. A conflict's id is an opaque key, not a UUID; GET .../conflicts lists the conflicts that are still open."
                }
              },
              "required": [
                "type",
                "id"
              ]
            },
            "description": "Knowledge base items (records, people, matches, conflicts, findings) the user attached to this message, each once. Empty for replies."
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "role",
          "text",
          "citations",
          "attachments",
          "attached_items",
          "created_at"
        ]
      },
      "description": "One page of messages, oldest first."
    },
    "next_cursor": {
      "description": "Pass as ?cursor to get the next page of messages. Null on the last page.",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "title",
    "created_at",
    "updated_at",
    "message_count",
    "messages",
    "next_cursor"
  ]
}
patch/workspaces/{workspace_id}/conversations/{conversation_id}

Rename a conversation

Only the title can be changed. Replays of an Idempotency-Key return the original body and request_id; headers are not replayed.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
conversation_idpathyesThe Research Companion conversation id.
Idempotency-KeyheadernoMakes the write safe to retry (1-255 characters, kept 24 hours). Same key and same request replays the original response; a different request with the same key is a 409.
Request body (application/json, required)
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "1 to 200 characters after trimming."
    }
  },
  "required": [
    "title"
  ],
  "additionalProperties": false
}

Responses

  • 200 The conversation. (Conversation)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan, or the key is read-only (a read_write key is required). (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. Also: the conversation is not in this workspace. (ErrorResponse)
  • 409 The workspace is archived (writes are refused). Also: Idempotency-Key was reused with a different request, or a request with it is still in progress. (ErrorResponse)
  • 422 Validation failed. A bad Idempotency-Key (1-255 characters) is also a 422. Also: Invalid body. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
200 response schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "title": {
      "type": "string"
    },
    "created_at": {
      "type": "string"
    },
    "updated_at": {
      "type": "string",
      "description": "Changes when a message is added or the conversation is renamed."
    },
    "message_count": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991,
      "description": "Messages the user sees in the conversation (their own and the companion's replies)."
    }
  },
  "required": [
    "id",
    "title",
    "created_at",
    "updated_at",
    "message_count"
  ]
}
delete/workspaces/{workspace_id}/conversations/{conversation_id}

Delete a conversation

Deletes the conversation, its messages and its attached files, as deleting it in the app does. It cannot be undone. Replays of an Idempotency-Key return the original body and request_id; headers are not replayed.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
conversation_idpathyesThe Research Companion conversation id.
Idempotency-KeyheadernoMakes the write safe to retry (1-255 characters, kept 24 hours). Same key and same request replays the original response; a different request with the same key is a 409.

Responses

  • 200 Deleted. (Ok)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan, or the key is read-only (a read_write key is required). (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. Also: the conversation is not in this workspace. (ErrorResponse)
  • 409 The workspace is archived (writes are refused). Also: Idempotency-Key was reused with a different request, or a request with it is still in progress. (ErrorResponse)
  • 422 Validation failed. A bad Idempotency-Key (1-255 characters) is also a 422. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
200 response schema
{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "ok"
  ]
}
get/workspaces/{workspace_id}/conversations/{conversation_id}/files/{file_id}

Get a download URL for an attached file

A signed URL for the file as it was uploaded. It works for 5 minutes; ask again for a new one.

NameInRequiredDescription
workspace_idpathyesThe workspace (project) id.
conversation_idpathyesThe Research Companion conversation id.
file_idpathyesThe id of a file attached to a message (attachments[].file_id).

Responses

  • 200 The signed URL. (ConversationFileUrl)
  • 401 Missing, malformed, unknown or revoked API key. (ErrorResponse)
  • 403 The account has no paid plan. (ErrorResponse)
  • 404 The workspace does not exist or is not visible to this key's owner. Also: the conversation is not in this workspace, or the file is not attached to it. (ErrorResponse)
  • 429 Rate limit exceeded. Wait the number of seconds in Retry-After. (ErrorResponse)
  • 500 Unexpected error. Retry later and quote request_id if it persists. (ErrorResponse)
200 response schema
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "format": "uri",
      "description": "Signed URL of the attached file, as the user uploaded it."
    },
    "expires_at": {
      "type": "string",
      "description": "The URL stops working at this time (5 minutes after issue)."
    }
  },
  "required": [
    "url",
    "expires_at"
  ]
}

We use cookies and similar technologies. Essential cookies keep the site working and secure. We only load analytics (PostHog, including masked session replays) and marketing (Meta Pixel and TikTok Pixel) with your consent. See our Privacy Policy.