Skip to content

API

Early Access

This feature is currently in early access.
Based on demand, we're opening spots as we go.
Contact us here to be added to the waitlist.

The conversation API is available at POST /api/chat.

It follows the OpenAI Responses API format as closely as possible: message objects, input_text and output_text content, responses, and response.* SSE events. Haloon's request envelope is still specific: it uses thread_id, model, and messages instead of input.

Security

Every call to /api/* must include the API key provided to you:

http
Authorization: Bearer ak-your_key

Also add this header to state-changing calls, including POST /api/chat:

http
Csrf-Token: nocheck

Haloon applies CSRF protection to HTTP requests. This protection is necessary for cookie-based web sessions, but an API client authenticates with a Bearer key and does not have a CSRF session token. Csrf-Token: nocheck enables the bypass intended for API clients; it does not replace API key authentication and should not be treated as a security mechanism.

The key starts with ak-. Never expose it in frontend code, a Git repository, a URL, or logs. Store it in an environment variable or secret manager. A missing, malformed, or unknown key does not authenticate the request.

bash
export HALOON_API_KEY='ak-your_key'

Create a conversation with an LLM

The messages parameter uses OpenAI Responses API input items. For text, send a message object containing input_text.

Create a thread_id

A thread_id is required. Its canonical format is thd-<uuid>, for example thd-550e8400-e29b-41d4-a716-446655440000.

Its behavior depends on the value:

  • If you send an existing thread_id, the message is added to that conversation.
  • If you send a new identifier in the thd-<uuid> format, the thread is created automatically with that identifier. This is the recommended approach because you immediately know which identifier to reuse.
  • If you send any other string, a thread is also created automatically, but with a random thread_id in the expected format. This identifier is not returned by POST /api/chat.

Prefer generating the identifier client-side before the first call, then reuse it for every turn.

Send the first message

The following example disables streaming to receive a single JSON object in the Responses API format.

bash
curl --request POST "https://haloon.ai/api/chat" \
  --header "Authorization: Bearer $HALOON_API_KEY" \
  --header 'Csrf-Token: nocheck' \
  --header 'Content-Type: application/json' \
  --data '{
    "thread_id": "'"$THREAD_ID"'",
    "model": "gpt-fast",
    "stream": false,
    "messages": [
      {
        "type": "message",
        "role": "user",
        "content": [
          { "type": "input_text", "text": "Hello, can you tell me a joke?" }
        ]
      }
    ]
  }'

The response follows the structure of an OpenAI response: it includes id, status, model, and output, as well as usage and error when available.

Reply in the same conversation

Reuse the same thread_id and send the relevant conversation history. The previous assistant message uses the OpenAI output_text format, then the new user message uses input_text.

bash
curl --request POST "https://haloon.ai/api/chat" \
  --header "Authorization: Bearer $HALOON_API_KEY" \
  --header 'Csrf-Token: nocheck' \
  --header 'Content-Type: application/json' \
  --data '{
    "thread_id": "'"$THREAD_ID"'",
    "model": "gpt-fast",
    "stream": false,
    "messages": [
      {
        "type": "message",
        "role": "user",
        "content": [
          { "type": "input_text", "text": "Hello, can you tell me a joke?" }
        ]
      },
      {
        "type": "message",
        "role": "assistant",
        "status": "completed",
        "content": [
          { "type": "output_text", "text": "Of course — here is a short one:\n\nWhy do divers always fall backwards and never forwards?\n\nBecause otherwise they would still be in the boat!" }
        ]
      },
      {
        "type": "message",
        "role": "user",
        "content": [
          { "type": "input_text", "text": "I know that one already, another please." }
        ]
      }
    ]
  }'

To receive the response progressively, omit stream or set it to true, then add --no-buffer to curl. The response is an SSE stream: each data: line contains an OpenAI-format event, such as response.output_text.delta; the stream ends with data: [DONE].

bash
curl --no-buffer --request POST "https://haloon.ai/api/chat" \
  --header "Authorization: Bearer $HALOON_API_KEY" \
  --header 'Csrf-Token: nocheck' \
  --header 'Content-Type: application/json' \
  --data '{
    "thread_id": "'"$THREAD_ID"'",
    "model": "gpt-fast",
    "messages": [
      {
        "type": "message",
        "role": "user",
        "content": [
          { "type": "input_text", "text": "Write a short tagline." }
        ]
      }
    ]
  }'

Parameters

ParameterTypeRequiredDescription
thread_idstringyesStable conversation identifier to reuse for subsequent turns.
modelstringyesHaloon preset identifier, such as gpt-fast, gpt-thinking, gpt-codex, gpt-5.6-sol, or gpt-5.6-terra.
messagesarrayyesConversation history, using OpenAI Responses API message items.
streambooleannoDefaults to true: an SSE response. Set to false for a single JSON response.
image_configobjectnoImage-generation options, used only with a compatible image model.

Generation settings (instructions, temperature, reasoning, tools, and token limits) are configured by the selected preset. The OpenAI parameters with the same names are therefore not directly exposed by this endpoint.

image_config options

image_config is sent to the provider for image-generation presets, such as gpt-image, nano-banana, flux-2, or recraft-fast. It is ignored for conversation models. Exact supported values depend on the selected model.

FieldValues / formatDefault
actiongenerate, edit, autoauto
backgroundtransparent, opaque, automodel-dependent
image_countinteger1
output_compressioninteger from 0 to 100model-dependent
sizefor example 1024x1024, 1024x1536, 1536x1024, or auto1024x1024
aspect_ratiofor example 1:1, 4:3, 3:4, 16:9, 9:16, or automodel-dependent
resolution512, 1K, 2K, 4Kmodel-dependent
qualityauto, low, medium, highauto
output_formatpng, jpeg, webp, svgmodel-dependent
response_formatBASE64, URLmodel-dependent

For example, to generate a square image with a transparent background:

json
{
  "thread_id": "thd-xxx",
  "model": "gpt-image",
  "stream": false,
  "image_config": {
    "action": "generate",
    "background": "transparent",
    "size": "1024x1024",
    "aspect_ratio": "1:1",
    "quality": "high",
    "output_format": "png"
  },
  "messages": [
    {
      "type": "message",
      "role": "user",
      "content": [
        { "type": "input_text", "text": "A minimalist blue fox logo" }
      ]
    }
  ]
}

Conversation management

List threads

Use GET /api/threads to list the authenticated user's threads. The response is paginated and the threads are in the data field.

bash
curl --request GET "https://haloon.ai/api/threads?limit=30&offset=0&sort_by=created_at&order_by=desc" \
  --header "Authorization: Bearer $HALOON_API_KEY"
ParameterDefaultDescription
sort_bycreated_atField used for sorting.
order_bydescSort order: asc or desc.
global_searchGlobal search term.
offset0Number of items to skip.
limit30Maximum number of returned items.

If you provided an arbitrary string when creating the conversation, search with created_at in desc order and retrieve the first thread's id, in the thd-<uuid> format.

List a thread's messages

Use GET /api/threads/:thread_id/messages to retrieve a conversation's messages. Pagination works the same way.

bash
curl --request GET "https://haloon.ai/api/threads/$THREAD_ID/messages" \
  --header "Authorization: Bearer $HALOON_API_KEY"

Want to join the API private beta? Contact us to join the waiting list.