API Reference

Overview

The Korey API is a REST API that lets you build integrations on top of Korey threads and messages. All requests are made to:

https://api.korey-staging.ai/api/v1-alpha0

All endpoints require a Bearer token and return JSON. Get started by creating a personal access token at Settings → API Tokens.

Authentication

All endpoints require a Bearer token passed in the Authorization header:

Authorization: Bearer kt_pat_<token>

Create a personal access token at Settings → API Tokens. Each token carries a set of scopes that gate access to specific endpoints. If a token lacks the required scope for an endpoint, the response is 403 Forbidden with a required_scope field indicating what is needed.

Thread State Lifecycle

Every thread has a state field that indicates whether it is ready to accept new messages or is currently processing a response.

State Meaning
ready The thread is idle and accepts new messages via POST /threads/:id/messages
waiting A message has been queued and is awaiting the AI processor
active The LLM is actively generating a response
error Processing failed; inspect the response for details
interrupted Generation was interrupted (e.g. by a conflicting message)

You may only send a new message when the thread is in ready state. Sending while waiting or active returns 409 Conflict.

The simplest way to know when a thread is ready again after sending a message is to open the SSE stream (GET /threads/:id/messages/:msg-id/response/stream) and wait for the done event — it is only emitted after the thread returns to ready. Alternatively, poll GET /threads/:id and check the state field.

Feedback

You can rate any AI response with a thumbs up or down via POST /threads/:id/messages/:msg-id/feedback. Optionally include a short free-text comment. One rating per user per message — submitting again updates the previous rating.

Conversational Continuity

Threads are persistent conversations. For multi-turn interactions, reuse the same thread by sending subsequent messages to POST /threads/:thread-id/messages rather than creating a new thread each time. Store the thread_id from the initial response and pass it to all follow-up messages.

Every thread and message includes an app_url field — a direct link to that item in the Korey web app, useful for surfacing context to users.

Set a meaningful name when creating a thread. It appears in the Korey UI and helps users identify the conversation later.

Error State Recovery

A thread in error state is still readable — you can fetch its messages via GET /threads/:id/messages — but it cannot receive new messages. There is currently no way to resume or reset a thread in error state.

To continue work after an error, create a new thread and seed it with a summary of the previous conversation. A useful pattern:

  1. Fetch the messages from the errored thread.
  2. Summarize the relevant context (e.g. what was being worked on, what was decided, where it left off).
  3. Create a new thread with that summary as the initial message.

Versioning

This API is currently at v1-alpha0. Breaking changes may occur without notice while in alpha. The path prefix will change to /api/v1 when the API graduates from alpha.

Identity

GET /me

Get current user

Returns the authenticated user's identity and organization.

Responses

StatusDescription
200 Success
401 Unauthorized — Bearer token required or invalid
403 Forbidden — token does not have required scope
200 response body
{
  /** JWT subject — stable string ID for the user. */
  sub: string;
  /** Display name of the user. */
  name: string | null;
  /** Email address of the user. */
  email: string | null;
  /** Internal numeric user ID. */
  korey_user_id: integer;
  /** Internal numeric organization ID. */
  korey_organization_id: integer;
  /** URL-safe organization slug. */
  korey_organization_slug: string;
  /** User role within the organization. */
  role: string;
}

Threads

GET /threads/{thread-id}/messages

List messages

Lists active messages in a thread, ordered by created_at ASC. Pass last_id as ?after= to paginate.

Path parameters

NameTypeDescription
thread-id required string The thread ID.

Query parameters

NameTypeDescription
limit integer Maximum number of messages to return. Default: 50, max 200.
after string Return messages after this message ID (for pagination).

Responses

StatusDescription
200 Success
401 Unauthorized — Bearer token required or invalid
403 Forbidden — token does not have required scope
404 Not found
200 response body
{
  /** Array of message objects. */
  data: [
    {
      /** Unique message ID. */
      id: string;
      /** ID of the thread this message belongs to. */
      thread_id: string;
      /** Who sent the message. */
      role: "user" | "assistant" | "summary";
      /** Ordered list of content blocks that make up the message. */
      contents: [
        {
            type: "text";
            text: string;
          }
        | {
            type: "image";
            attachment_id: string;
          }
        | {
            type: "document";
            attachment_id: string;
          }
        | {
            type: "user_get_choice";
            tool_use_id: string;
            question: string;
            choices: string[];
          }
      ];
      /** ISO 8601 timestamp when the message was created. */
      created_at: string;
      /** Direct link to this message in the Korey web app. */
      app_url: string;
    }
  ];
  /** ID of the first message in the page. */
  first_id: string | null;
  /** ID of the last message in the page. Pass as ?after= to fetch the next page. */
  last_id: string | null;
  /** True when there are more messages beyond this page. */
  has_more: boolean;
  /** The limit applied to this request. */
  limit: integer;
}
POST /threads/{thread-id}/messages

Send message

Sends a user text message to a thread and triggers AI processing.

The thread must be in ready state — the only state in which new messages are accepted. Returns 409 if the thread is not ready.

After sending, the thread transitions:

  • waiting — message is queued for the AI processor
  • active — the LLM is generating a response
  • ready — response is complete; the thread accepts new messages again

Use the SSE stream endpoint or poll the thread state field to know when the thread returns to ready.

Path parameters

NameTypeDescription
thread-id required string The thread ID.

Request body

{
  /** The user message text to send. */
  text: string;
}

Responses

StatusDescription
201 Created
401 Unauthorized — Bearer token required or invalid
403 Forbidden
404 Not found
409 Conflict
201 response body
{
  /** ID of the newly created message. Use this to poll or stream the AI response. */
  message_id: string;
}
POST /threads/{thread-id}/messages/{message-id}/feedback

Submit feedback

Submits a thumbs-up or thumbs-down rating for an AI message. One feedback entry per user per message — submitting again updates the previous rating.

Path parameters

NameTypeDescription
thread-id required string The thread ID.
message-id required string The message ID.

Request body

{
  /** True for thumbs up, false for thumbs down. */
  positive: boolean;
  /** Optional free-text comment accompanying the rating. */
  feedback_text?: string;
}

Responses

StatusDescription
204 Response
401 Unauthorized — Bearer token required or invalid
403 Forbidden — token does not have required scope
204 response body
GET /threads/{thread-id}/messages/{message-id}/response/stream

Stream AI response

SSE stream for the AI response to a user message.

Events are sent in SSE format:

event: <type>
data: <json>
Event Data Description
token {"token": "..."} A text chunk of the AI response
error {"message": "..."} An error occurred
done {} Response is fully complete — thread is back in ready state

done is only emitted after the LLM has finished generating and the thread has returned to ready. Once you receive done the thread accepts new messages. Thinking and status events are filtered. The stream closes after done or error.

Path parameters

NameTypeDescription
thread-id required string The thread ID.
message-id required string The message ID.

Responses

StatusDescription
401 Unauthorized — Bearer token required or invalid
403 Forbidden — token does not have required scope
GET /threads

List threads

Lists visible threads for the authenticated user. Pass last_id as ?after= to paginate.

Query parameters

NameTypeDescription
owned boolean true = only threads you own; false = threads owned by others; omit = all visible threads.
private boolean true = private threads only; false = public threads only; omit = both.
archived boolean true to include archived threads. Default: false.
q string Full-text search query against thread names and messages.
sort_by "updated_at" | "created_at" Field to sort by. Default: updated_at.
sort_dir "asc" | "desc" Sort direction. Default: desc.
updated_since string ISO 8601 timestamp — return only threads updated after this time.
limit integer Maximum number of threads to return. Default: 50, max 200.
after string Return threads after this thread ID (for pagination).

Responses

StatusDescription
200 Success
401 Unauthorized — Bearer token required or invalid
403 Forbidden — token does not have required scope
200 response body
{
  /** Array of thread objects. */
  data: [
    {
      /** Unique thread ID. */
      id: string;
      /** Optional display name for the thread. */
      name: string | null;
      /** Current processing state of the thread. */
      state: "ready" | "waiting" | "active" | "error" | "interrupted";
      /** When true the thread is only visible to its owner. */
      is_private: boolean;
      /** Whether the thread has been archived. */
      archived: boolean;
      /** The user who created the thread. */
      owner: {
        /** Owner user ID. */
        id: string;
        /** Owner display name. */
        name: string | null;
      };
      /** ISO 8601 timestamp when the thread was created. */
      created_at: string;
      /** ISO 8601 timestamp when the thread was last updated. */
      updated_at: string;
      /** Direct link to this thread in the Korey web app. */
      app_url: string;
    }
  ];
  /** ID of the first item in the page. null when the page is empty. */
  first_id: string | null;
  /** ID of the last item in the page. Pass as ?after= to fetch the next page. */
  last_id: string | null;
  /** True when there are more results beyond this page. */
  has_more: boolean;
  /** The limit applied to this request. */
  limit: integer;
}
POST /threads

Create thread

Creates a new thread with an initial message. Returns 409 on conflict.

Request body

{
  /** Initial message text to start the thread. */
  text: string;
  /** Optional display name for the thread. */
  name?: string | null;
  /** When true the thread is only visible to its owner. Defaults to false. */
  is_private?: boolean;
}

Responses

StatusDescription
201 Created
401 Unauthorized — Bearer token required or invalid
403 Forbidden — token does not have required scope
409 Conflict
201 response body
{
  /** ID of the newly created thread. */
  thread_id: string;
  /** ID of the initial message. */
  message_id: string;
}
GET /threads/{thread-id}

Get thread

Returns a single thread by ID. 404 for private threads not owned by the caller.

Path parameters

NameTypeDescription
thread-id required string The thread ID.

Responses

StatusDescription
200 Success
401 Unauthorized — Bearer token required or invalid
403 Forbidden — token does not have required scope
404 Not found
200 response body
{
  /** Unique thread ID. */
  id: string;
  /** Optional display name for the thread. */
  name: string | null;
  /** Current processing state of the thread. */
  state: "ready" | "waiting" | "active" | "error" | "interrupted";
  /** When true the thread is only visible to its owner. */
  is_private: boolean;
  /** Whether the thread has been archived. */
  archived: boolean;
  /** The user who created the thread. */
  owner: {
    /** Owner user ID. */
    id: string;
    /** Owner display name. */
    name: string | null;
  };
  /** ISO 8601 timestamp when the thread was created. */
  created_at: string;
  /** ISO 8601 timestamp when the thread was last updated. */
  updated_at: string;
  /** Direct link to this thread in the Korey web app. */
  app_url: string;
}
GET /threads/{thread-id}/messages/{message-id}/response

Poll for AI response

Polls for the AI response to a user message.

Returns 202 Accepted while the thread is in waiting or active state. Returns 200 OK once the thread returns to ready and the full response is available. Returns 404 if no response exists for the message.

Prefer the SSE stream endpoint for lower latency.

Path parameters

NameTypeDescription
thread-id required string The thread ID.
message-id required string The message ID.

Responses

StatusDescription
200 Success
202 Accepted
401 Unauthorized — Bearer token required or invalid
403 Forbidden — token does not have required scope
404 Not found
200 response body
{
  status: "complete";
  messages: [
    {
      /** Unique message ID. */
      id: string;
      /** ID of the thread this message belongs to. */
      thread_id: string;
      /** Who sent the message. */
      role: "user" | "assistant" | "summary";
      /** Ordered list of content blocks that make up the message. */
      contents: [
        {
            type: "text";
            text: string;
          }
        | {
            type: "image";
            attachment_id: string;
          }
        | {
            type: "document";
            attachment_id: string;
          }
        | {
            type: "user_get_choice";
            tool_use_id: string;
            question: string;
            choices: string[];
          }
      ];
      /** ISO 8601 timestamp when the message was created. */
      created_at: string;
      /** Direct link to this message in the Korey web app. */
      app_url: string;
    }
  ];
}
202 response body
{
  status: "processing";
}