API reference
The API is in early access and may still change before general availability. We tell early-access teams before anything breaks.
All requests and responses are JSON. The base URL comes with your access ($TOOLCUE_URL in the examples).
Authentication#
Send a token in the Authorization header:
Authorization: Bearer $TOOLCUE_TOKEN| Token | Use it for |
|---|---|
| Read token | The per-turn calls: POST /v1/context, POST /v1/find-tools, and reading an agent |
| Admin token | Publishing: catalogs, past conversations, deleting an agent |
Keep both on your server. Call Toolcue from your backend, never from a browser: the request carries your user's roles, and the tokens give access to your catalogs.
Agents#
An agent is one catalog of tools under an id you choose (acme-billing; it goes in URLs, so stick to letters, digits, - and _). Use one agent per assistant, and one per tenant if your tenants have different tools. Toolcue learns per agent and never mixes them.
Publish a catalog#
PUT /v1/agents/{agentId}/catalog (admin token)
Replaces the agent's whole catalog. Publish again whenever your tools change; it takes effect for the next request.
{
"tools": [
{
"name": "refund_invoice",
"displayName": "Refund an invoice",
"description": "Refund a paid invoice fully or partially.",
"parameters": [
{"name": "invoice_id", "description": "The invoice id, e.g. INV-1042", "dataType": "string", "required": true},
{"name": "amount", "description": "Amount to refund; omit for a full refund", "dataType": "number", "required": false}
],
"examples": ["give them their money back", "refund INV-1042"]
},
{
"name": "find_customer",
"description": "Find a customer by name, email or domain.",
"parameters": [{"name": "query", "dataType": "string", "required": true}],
"produces": ["customer_id"]
}
]
}| Field | Type | |
|---|---|---|
tools | array | Your tools. |
tools[].name | string | Required. The tool's name, exactly as your model sees it. Responses use it. |
tools[].description | string | Required. What the tool does, in plain words. The most important field for matching. |
tools[].displayName | string | Optional. A human-readable name. |
tools[].parameters | array | Optional. name, description, dataType, required. Required id parameters (invoice_id) are how Toolcue knows a lookup is needed. |
tools[].examples | string[] | Optional, up to 100. How users ask for it, in their words. |
tools[].produces | string[] | Optional. Ids the tool returns (customer_id). Makes it the lookup for every tool that needs that id. |
tools[].core | boolean | Optional. Attach on every turn (e.g. "ask the user a question"). |
tools[].requiredRoles | string[] | Optional. Only users with one of these roles get the tool (see context.user.roles). |
Response:
{"agentId": "acme-billing", "tools": 9, "edges": 5, "traces": 0,
"edgesBySource": {"DECLARED": 4, "STATIC": 1, "TRACE": 0}}edges counts the dependency links Toolcue found between your tools: DECLARED from produces, STATIC inferred from names and parameters, TRACE learned from past conversations. Other counts may appear; ignore them.
Add past conversations#
POST /v1/agents/{agentId}/traces (admin token)
Optional. Conversations from your logs, so Toolcue learns which tools usually follow which ("find customer, then cancel subscription"). A pattern counts once it repeats across separate conversations. Messages have the same shape as history in POST /v1/context.
[
{"id": "conv-1029",
"messages": [
{"role": "user", "content": "cancel Globex's subscription"},
{"role": "assistant", "content": "Done.", "toolCalls": [
{"name": "find_customer", "arguments": {"query": "Globex"}, "result": "{\"customer_id\": \"cus_8812\"}"},
{"name": "cancel_subscription", "arguments": {"customer_id": "cus_8812"}}]}]}
]Answers with the same counts as publishing.
Read an agent#
| Call | Token | Answers |
|---|---|---|
GET /v1/agents/{agentId} | read | The counts above |
GET /v1/agents/{agentId}/catalog | read | The catalog as published |
GET /v1/agents/{agentId}/graph | read | {"edges": [{"provider", "consumer", "param", "confidence", "source"}]}: which tool supplies which id to which |
DELETE /v1/agents/{agentId} | admin | 204; the catalog, the learned links and the conversations are gone |
Get the tools for a turn#
POST /v1/context (read token)
Call it once per user message, before your LLM call, and keep the result for the whole turn (every model call in your tool loop).
{
"agentId": "acme-billing",
"message": "ok, move them to the scale plan",
"history": [
{"role": "user", "content": "what plan is Globex on?"},
{"role": "assistant", "content": "Globex (cus_8812) is on growth.",
"toolCalls": [{"name": "find_customer", "arguments": {"query": "Globex"},
"result": "{\"customer_id\": \"cus_8812\", \"plan\": \"growth\"}"}]}
],
"context": {"user": {"roles": ["agent"]}},
"options": {"includePrompt": false}
}| Field | Type | |
|---|---|---|
agentId | string | Required. |
message | string | Required. The user's new message. |
history | array | Optional. Earlier messages, oldest first: role (user or assistant), content, and toolCalls (name, arguments, result as a string). The last 10 or so are plenty. |
context.user | object | Optional. roles (string array) for role-restricted tools, plus any attributes you like. Set it from your authenticated session. |
context.page | object | Optional. url and title of the page the user is on, for in-app assistants. |
options.maxTools | integer | Optional, 0–50. A cap on matched tools; lookups and always-on tools come on top. |
options.includePrompt | boolean | Set it to false. |
Response (the timing is from a laptop; the first message on a new topic is the slowest):
{
"agentId": "acme-billing",
"turnType": "ACTION",
"tools": [
{"name": "change_plan", "score": 0.44, "reason": "RETRIEVED", "detail": "matched the turn"},
{"name": "find_customer", "score": 0.01, "reason": "STICKY", "detail": "called in a recent turn"},
{"name": "create_invoice", "score": 0.03, "reason": "STICKY", "detail": "acts on the record find_customer returned"},
{"name": "cancel_subscription", "score": 0.02, "reason": "STICKY", "detail": "acts on the record find_customer returned"},
{"name": "get_customer_invoices", "score": 0.01, "reason": "STICKY", "detail": "provides invoice_id for refund_invoice"},
{"name": "export_report", "score": 0.05, "reason": "RETRIEVED", "detail": "one of the best matches for a request"},
{"name": "ask_user", "score": 0.04, "reason": "RETRIEVED", "detail": "one of the best matches for a request"},
{"name": "refund_invoice", "score": 0.01, "reason": "RERANKED", "detail": "reranker"}
],
"resolvedParams": {"customer_id": "cus_8812"},
"timings": {"totalMs": 103.4}
}| Field | |
|---|---|
tools | The tools to pass your model, in order. name is your tool's name; reason says why it is there; detail explains it in words; score is for logging. |
turnType | SMALLTALK (only your core tools), KNOWLEDGE, ACTION or MIXED. |
resolvedParams | Ids found in the conversation (customer_id). Put them in your prompt so the model does not look them up again. |
timings.totalMs | Time spent inside Toolcue. |
Other fields may appear in the response; ignore them.
reason | Why the tool is there |
|---|---|
RETRIEVED | It matches what the user asked |
DEPENDENCY | It supplies an id a selected tool needs |
STICKY | It was used in the last turns, or acts on the record a recent tool returned |
RERANKED | A close match kept as a safety net |
CORE | You marked it always-on |
PAGE | It is tied to the page the user is on (only when you send context.page) |
Search the whole catalog#
POST /v1/find-tools (read token)
Back a find_tools tool with this call, so the model can get a tool the selection left out, in the same turn. The same role rules apply.
{"agentId": "acme-billing", "query": "set the VAT rate to 20%",
"exclude": ["refund_invoice", "ask_user"], "limit": 3,
"context": {"user": {"roles": ["admin"]}}}| Field | |
|---|---|
query | Required. The task in words, as the model describes it. |
exclude | Optional. Tools the model already has. |
limit | Optional, 1–50 (default 5). Lookups the found tools need come on top. |
Response: tools (same shape as above, with RETRIEVED or DEPENDENCY) and definitions, a ready-made JSON tool definition for each, built from your catalog. Most integrations use only the names.
Errors and limits#
Errors answer with a status and {"error": "..."}:
| Status | When |
|---|---|
400 | Invalid input; the message says which field |
401 | Missing or wrong token, or a read token on a publishing call |
404 | Unknown agent: {"error": "unknown agent: acme-billing"} |
413 | Body too large: 8 MB for publishing, 256 KB for everything else |
429 | Too many requests; wait for the seconds in Retry-After |
Fail open. If Toolcue is slow or unreachable, send all your tools for that turn. Set a short timeout (the example clients use 2 seconds) and fall back: your agent never depends on Toolcue to answer.