Messages endpoint
One endpoint does it all: answers, conversations, reasoning, tool calls and files. You send the conversation so far, and Kalle sends back the next message.
POST/v1/messages
A conversation
The API keeps no memory between requests. Send the whole conversation each time, oldest message first, with roles that alternate between user and assistant. Use system for instructions that apply to the whole conversation.
curl https://api.kalle.build/v1/messages \
-H "Authorization: Bearer $KALLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kalle-turbo-max",
"max_tokens": 1024,
"system": "You answer questions for Nordlys Coffee. Keep every answer to two sentences.",
"messages": [
{ "role": "user", "content": "Do you ship to Norway?" },
{
"role": "assistant",
"content": "Yes, we ship to Norway from our warehouse in Aarhus."
},
{ "role": "user", "content": "How long does it take?" }
]
}'const response = await fetch("https://api.kalle.build/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KALLE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "kalle-turbo-max",
max_tokens: 1024,
system: "You answer questions for Nordlys Coffee. Keep every answer to two sentences.",
messages: [
{ role: "user", content: "Do you ship to Norway?" },
{
role: "assistant",
content: "Yes, we ship to Norway from our warehouse in Aarhus."
},
{ role: "user", content: "How long does it take?" }
]
}),
});
console.log(await response.json());import os
import requests
response = requests.post(
"https://api.kalle.build/v1/messages",
headers={"Authorization": f"Bearer {os.environ['KALLE_API_KEY']}"},
json={
"model": "kalle-turbo-max",
"max_tokens": 1024,
"system": "You answer questions for Nordlys Coffee. Keep every answer to two sentences.",
"messages": [
{"role": "user", "content": "Do you ship to Norway?"},
{
"role": "assistant",
"content": "Yes, we ship to Norway from our warehouse in Aarhus."
},
{"role": "user", "content": "How long does it take?"}
]
},
)
response.raise_for_status()
print(response.json())package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
body := strings.NewReader(`{
"model": "kalle-turbo-max",
"max_tokens": 1024,
"system": "You answer questions for Nordlys Coffee. Keep every answer to two sentences.",
"messages": [
{ "role": "user", "content": "Do you ship to Norway?" },
{
"role": "assistant",
"content": "Yes, we ship to Norway from our warehouse in Aarhus."
},
{ "role": "user", "content": "How long does it take?" }
]
}`)
req, _ := http.NewRequest("POST", "https://api.kalle.build/v1/messages", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KALLE_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}require "json"
require "net/http"
uri = URI("https://api.kalle.build/v1/messages")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("KALLE_API_KEY")}"
request["Content-Type"] = "application/json"
request.body = <<~JSON
{
"model": "kalle-turbo-max",
"max_tokens": 1024,
"system": "You answer questions for Nordlys Coffee. Keep every answer to two sentences.",
"messages": [
{ "role": "user", "content": "Do you ship to Norway?" },
{
"role": "assistant",
"content": "Yes, we ship to Norway from our warehouse in Aarhus."
},
{ "role": "user", "content": "How long does it take?" }
]
}
JSON
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts JSON.pretty_generate(JSON.parse(response.body))<?php
$body = <<<'JSON'
{
"model": "kalle-turbo-max",
"max_tokens": 1024,
"system": "You answer questions for Nordlys Coffee. Keep every answer to two sentences.",
"messages": [
{ "role": "user", "content": "Do you ship to Norway?" },
{
"role": "assistant",
"content": "Yes, we ship to Norway from our warehouse in Aarhus."
},
{ "role": "user", "content": "How long does it take?" }
]
}
JSON;
$ch = curl_init("https://api.kalle.build/v1/messages");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KALLE_API_KEY"),
"Content-Type: application/json",
],
]);
echo curl_exec($ch);import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class KalleExample {
public static void main(String[] args) throws Exception {
String body = """
{
"model": "kalle-turbo-max",
"max_tokens": 1024,
"system": "You answer questions for Nordlys Coffee. Keep every answer to two sentences.",
"messages": [
{ "role": "user", "content": "Do you ship to Norway?" },
{
"role": "assistant",
"content": "Yes, we ship to Norway from our warehouse in Aarhus."
},
{ "role": "user", "content": "How long does it take?" }
]
}
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.kalle.build/v1/messages"))
.header("Authorization", "Bearer " + System.getenv("KALLE_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}Request body
| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | kalle-turbo, kalle-design, kalle-turbo-max, kalle-turbo-ultra or auto. See Models. |
messages | array | Yes | The conversation so far. The first message has the role user. |
max_tokens | integer | Yes | The most output tokens Kalle may write. The answer stops when it gets there. |
system | string | No | Instructions for the whole conversation: tone, rules, background. |
effort | string | No | auto, low, medium, high, extra or max. Defaults to auto. |
stream | boolean | No | Send the answer as server-sent events. See Streaming. |
tools | array | No | Functions Kalle may call. See Tool use. |
tool_choice | string or object | No | auto, none, any, or { "type": "tool", "name": "..." } to require one tool. |
stop_sequences | array | No | Strings that end the answer as soon as Kalle writes one of them. |
metadata.user_id | string | No | Your own ID for the person behind the request. Never send names or email addresses. |
Message content
content is either a string or a list of blocks. A string is the same as one text block. Use blocks to mix text with files and images, or to send tool results.
{
"role": "user",
"content": [
{ "type": "text", "text": "Which of these two layouts is easier to scan?" },
{ "type": "image", "source": { "type": "url", "url": "https://example.com/a.png" } }
]
}Response
{
"id": "msg_01J8Z5",
"type": "message",
"role": "assistant",
"model": "kalle-turbo-max",
"content": [
{ "type": "text", "text": "Orders to Norway usually arrive in three to five working days." }
],
"stop_reason": "end_turn",
"usage": { "input_tokens": 58, "output_tokens": 14, "reasoning_tokens": 0 }
}| Field | Description |
|---|---|
id | A unique ID for this message. Include it when you contact support. |
model | The model that answered. With auto, this is the model Kalle picked. |
content | The answer as a list of blocks: text, and tool_use when Kalle calls a tool. |
stop_reason | Why Kalle stopped writing. See below. |
usage | Tokens you pay for: input_tokens, output_tokens and the reasoning_tokens included in the output. |
Stop reasons
| Value | Meaning |
|---|---|
end_turn | Kalle finished the answer. |
max_tokens | The answer reached max_tokens. Raise the limit or ask Kalle to continue. |
stop_sequence | Kalle wrote one of your stop_sequences. |
tool_use | Kalle wants to call a tool. Run it and send the result back. |
Tips for good answers
- Put stable instructions in
systemand the task itself in the lastusermessage. - Show Kalle an example of the output you want. One good example beats a paragraph of rules.
- Ask for JSON in a fixed shape when your code will read the answer, and check it before you use it.
- Keep conversations short. Every request pays for the whole history as input tokens, so summarise long threads.