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:
Authorization: Bearer ak-your_keyAlso add this header to state-changing calls, including POST /api/chat:
Csrf-Token: nocheckHaloon 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.
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_idin the expected format. This identifier is not returned byPOST /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.
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.
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].
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
| Parameter | Type | Required | Description |
|---|---|---|---|
thread_id | string | yes | Stable conversation identifier to reuse for subsequent turns. |
model | string | yes | Haloon preset identifier, such as gpt-fast, gpt-thinking, gpt-codex, gpt-5.6-sol, or gpt-5.6-terra. |
messages | array | yes | Conversation history, using OpenAI Responses API message items. |
stream | boolean | no | Defaults to true: an SSE response. Set to false for a single JSON response. |
image_config | object | no | Image-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.
| Field | Values / format | Default |
|---|---|---|
action | generate, edit, auto | auto |
background | transparent, opaque, auto | model-dependent |
image_count | integer | 1 |
output_compression | integer from 0 to 100 | model-dependent |
size | for example 1024x1024, 1024x1536, 1536x1024, or auto | 1024x1024 |
aspect_ratio | for example 1:1, 4:3, 3:4, 16:9, 9:16, or auto | model-dependent |
resolution | 512, 1K, 2K, 4K | model-dependent |
quality | auto, low, medium, high | auto |
output_format | png, jpeg, webp, svg | model-dependent |
response_format | BASE64, URL | model-dependent |
For example, to generate a square image with a transparent background:
{
"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.
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"| Parameter | Default | Description |
|---|---|---|
sort_by | created_at | Field used for sorting. |
order_by | desc | Sort order: asc or desc. |
global_search | — | Global search term. |
offset | 0 | Number of items to skip. |
limit | 30 | Maximum 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.
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.