toolcue Join the waitlist

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:

bash
Authorization: Bearer $TOOLCUE_TOKEN
TokenUse it for
Read tokenThe per-turn calls: POST /v1/context, POST /v1/find-tools, and reading an agent
Admin tokenPublishing: 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.

json
{
  "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"]
    }
  ]
}
FieldType
toolsarrayYour tools.
tools[].namestringRequired. The tool's name, exactly as your model sees it. Responses use it.
tools[].descriptionstringRequired. What the tool does, in plain words. The most important field for matching.
tools[].displayNamestringOptional. A human-readable name.
tools[].parametersarrayOptional. name, description, dataType, required. Required id parameters (invoice_id) are how Toolcue knows a lookup is needed.
tools[].examplesstring[]Optional, up to 100. How users ask for it, in their words.
tools[].producesstring[]Optional. Ids the tool returns (customer_id). Makes it the lookup for every tool that needs that id.
tools[].corebooleanOptional. Attach on every turn (e.g. "ask the user a question").
tools[].requiredRolesstring[]Optional. Only users with one of these roles get the tool (see context.user.roles).

Response:

json
{"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.

json
[
  {"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#

CallTokenAnswers
GET /v1/agents/{agentId}readThe counts above
GET /v1/agents/{agentId}/catalogreadThe catalog as published
GET /v1/agents/{agentId}/graphread{"edges": [{"provider", "consumer", "param", "confidence", "source"}]}: which tool supplies which id to which
DELETE /v1/agents/{agentId}admin204; 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).

json
{
  "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}
}
FieldType
agentIdstringRequired.
messagestringRequired. The user's new message.
historyarrayOptional. 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.userobjectOptional. roles (string array) for role-restricted tools, plus any attributes you like. Set it from your authenticated session.
context.pageobjectOptional. url and title of the page the user is on, for in-app assistants.
options.maxToolsintegerOptional, 0–50. A cap on matched tools; lookups and always-on tools come on top.
options.includePromptbooleanSet it to false.

Response (the timing is from a laptop; the first message on a new topic is the slowest):

json
{
  "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
toolsThe 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.
turnTypeSMALLTALK (only your core tools), KNOWLEDGE, ACTION or MIXED.
resolvedParamsIds found in the conversation (customer_id). Put them in your prompt so the model does not look them up again.
timings.totalMsTime spent inside Toolcue.

Other fields may appear in the response; ignore them.

reasonWhy the tool is there
RETRIEVEDIt matches what the user asked
DEPENDENCYIt supplies an id a selected tool needs
STICKYIt was used in the last turns, or acts on the record a recent tool returned
RERANKEDA close match kept as a safety net
COREYou marked it always-on
PAGEIt 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.

json
{"agentId": "acme-billing", "query": "set the VAT rate to 20%",
 "exclude": ["refund_invoice", "ask_user"], "limit": 3,
 "context": {"user": {"roles": ["admin"]}}}
Field
queryRequired. The task in words, as the model describes it.
excludeOptional. Tools the model already has.
limitOptional, 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": "..."}:

StatusWhen
400Invalid input; the message says which field
401Missing or wrong token, or a read token on a publishing call
404Unknown agent: {"error": "unknown agent: acme-billing"}
413Body too large: 8 MB for publishing, 256 KB for everything else
429Too 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.