Tool Calling
POST /v1/messages
Custom functions run in your application. The model supplies the function name and arguments; your code performs the lookup and returns the result.
Request and Authentication
POST https://api.tokatlas.ai/v1/messages
x-api-key: YOUR_API_KEY
anthropic-version: 2023-06-01
Content-Type: application/json{
"model": "claude-sonnet-4-6",
"tools": [
{
"name": "lookup_stock",
"description": "Look up available inventory for a product SKU.",
"input_schema": {
"type": "object",
"properties": {
"sku": {
"type": "string",
"description": "Product SKU, for example DEMO-001"
}
},
"required": [
"sku"
],
"additionalProperties": false
}
}
],
"max_tokens": 2048,
"messages": [
{
"role": "user",
"content": "Check stock for DEMO-001."
}
]
}Tool Call Format
Illustrative call item; IDs and arguments come from the actual response.
{
"type": "tool_use",
"id": "toolu_example",
"name": "lookup_stock",
"input": {
"sku": "DEMO-001"
}
}Read every
tool_useblock incontent. Return matchingtool_resultblocks in the next user message, preserving the entire assistant content. Put results before any user text.tool_choiceaccepts{"type":"auto"},{"type":"any"}, or{"type":"tool","name":"lookup_stock"}. Use automatic selection for the loop so the model can finish.For streaming, assemble
input_json_delta.partial_jsonby content-block index before parsing. Do not execute partial arguments.
First Request in Each Language
These requests obtain a tool-call request; they do not execute the tool. Use the complete workflow below to return results with the actual call IDs and continue the conversation.
See language setup. Set API_KEY and replace model, file URL, and ID placeholders first. Each version displays the raw response to the same request.
curl --fail-with-body --silent --show-error --max-time 180 \
--request POST \
--url "https://api.tokatlas.ai/v1/messages" \
--header "x-api-key: $API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "Content-Type: application/json" \
--data '{
"model": "claude-sonnet-4-6",
"tools": [
{
"name": "lookup_stock",
"description": "Look up available inventory for a product SKU.",
"input_schema": {
"type": "object",
"properties": {
"sku": {
"type": "string",
"description": "Product SKU, for example DEMO-001"
}
},
"required": [
"sku"
],
"additionalProperties": false
}
}
],
"stream": false,
"messages": [
{
"role": "user",
"content": "Check stock for DEMO-001."
}
],
"max_tokens": 2048
}'import os
import requests
headers = {
'x-api-key': os.environ["API_KEY"],
'anthropic-version': '2023-06-01',
'Content-Type': 'application/json',
}
payload = {'model': 'claude-sonnet-4-6',
'tools': [{'name': 'lookup_stock',
'description': 'Look up available inventory for a product SKU.',
'input_schema': {'type': 'object',
'properties': {'sku': {'type': 'string',
'description': 'Product SKU, for '
'example DEMO-001'}},
'required': ['sku'],
'additionalProperties': False}}],
'stream': False,
'messages': [{'role': 'user', 'content': 'Check stock for DEMO-001.'}],
'max_tokens': 2048}
response = requests.request(
'POST', 'https://api.tokatlas.ai/v1/messages', headers=headers,
json=payload,
timeout=180,
)
response.raise_for_status()
print(response.text)if (!process.env.API_KEY) throw new Error("Set API_KEY first.");
const response = await fetch("https://api.tokatlas.ai/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.API_KEY,
"anthropic-version": "2023-06-01",
"Content-Type": "application/json",
},
body: JSON.stringify({
"model": "claude-sonnet-4-6",
"tools": [
{
"name": "lookup_stock",
"description": "Look up available inventory for a product SKU.",
"input_schema": {
"type": "object",
"properties": {
"sku": {
"type": "string",
"description": "Product SKU, for example DEMO-001"
}
},
"required": [
"sku"
],
"additionalProperties": false
}
}
],
"stream": false,
"messages": [
{
"role": "user",
"content": "Check stock for DEMO-001."
}
],
"max_tokens": 2048
}),
signal: AbortSignal.timeout(180_000),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(await response.text());import java.net.URI;
import java.net.http.*;
import java.time.Duration;
public class Example {
public static void main(String[] args) throws Exception {
String apiKey = System.getenv("API_KEY");
if (apiKey == null || apiKey.isBlank()) {
throw new IllegalArgumentException("Set API_KEY first.");
}
String payload = String.join("\n",
"{",
" \"model\": \"claude-sonnet-4-6\",",
" \"tools\": [",
" {",
" \"name\": \"lookup_stock\",",
" \"description\": \"Look up available inventory for a product SKU.\",",
" \"input_schema\": {",
" \"type\": \"object\",",
" \"properties\": {",
" \"sku\": {",
" \"type\": \"string\",",
" \"description\": \"Product SKU, for example DEMO-001\"",
" }",
" },",
" \"required\": [",
" \"sku\"",
" ],",
" \"additionalProperties\": false",
" }",
" }",
" ],",
" \"stream\": false,",
" \"messages\": [",
" {",
" \"role\": \"user\",",
" \"content\": \"Check stock for DEMO-001.\"",
" }",
" ],",
" \"max_tokens\": 2048",
"}"
);
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30)).build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.tokatlas.ai/v1/messages"))
.timeout(Duration.ofSeconds(180))
.header("x-api-key", apiKey)
.header("anthropic-version", "2023-06-01")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(payload))
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new IllegalStateException("HTTP " + response.statusCode() + ": "
+ response.body());
}
System.out.println(response.body());
}
}Complete Python Example
Install requests (pip install requests) and set the API_KEY environment variable. The inventory result is demo data. Replace execute with your business service and select a model enabled for your account.
import json
import os
import requests
URL = "https://api.tokatlas.ai/v1/messages"
HEADERS = {'x-api-key': os.environ["API_KEY"], 'anthropic-version': '2023-06-01', 'Content-Type': 'application/json'}
TOOLS = [{'name': 'lookup_stock',
'description': 'Look up available inventory for a product SKU.',
'input_schema': {'type': 'object',
'properties': {'sku': {'type': 'string',
'description': 'Product SKU, for example '
'DEMO-001'}},
'required': ['sku'],
'additionalProperties': False}}]
def post(payload):
response = requests.post(URL, headers=HEADERS, json=payload, timeout=60)
response.raise_for_status()
body = response.json()
# Accept the documented gateway envelope or a direct protocol response.
data = body.get("data", body)
if not isinstance(data, dict):
raise RuntimeError("Unexpected API response")
if data.get("error"):
raise RuntimeError(data["error"])
return data
def execute(name, args):
if name != "lookup_stock":
return {"error": "Unknown tool"}
if not isinstance(args, dict) or set(args) != {"sku"}:
return {"error": "Expected exactly one sku argument"}
if not isinstance(args["sku"], str) or not args["sku"].strip():
return {"error": "sku must be a non-empty string"}
# Demo fixture only; replace with your inventory service.
stock = {"DEMO-001": 18}
if args["sku"] not in stock:
return {"error": "SKU not found"}
return {"sku": args["sku"], "available": stock[args["sku"]]}
messages = [{"role": "user", "content": "Check stock for DEMO-001."}]
for _ in range(6):
reply = post({"model": "claude-sonnet-4-6", "max_tokens": 2048,
"tools": TOOLS, "messages": messages})
if reply.get("stop_reason") == "max_tokens":
raise RuntimeError("Truncated response; increase max_tokens")
blocks = reply["content"]
calls = [b for b in blocks if b["type"] == "tool_use"]
if not calls:
print("\n".join(b["text"] for b in blocks if b["type"] == "text"))
break
messages.append({"role": "assistant", "content": blocks})
results = []
for call in calls:
result = execute(call["name"], call["input"])
results.append({"type": "tool_result", "tool_use_id": call["id"],
"content": json.dumps(result), "is_error": "error" in result})
messages.append({"role": "user", "content": results})
else:
raise RuntimeError("Tool round limit reached")Troubleshooting
| Symptom | Check |
|---|---|
| Missing tool result | Return one result for every call; retain the exact IDs. |
| Repeated calls | Avoid forcing a tool on every round; cap the loop. |
| Invalid arguments | Validate names and parameters before dispatching. |
| Tool failure | Return a structured error instead of invented data. |
| Unsupported option | Check the selected model and gateway route capabilities. |
