- Webhook tools — you host an HTTP endpoint, qlaud signs + POSTs to it, your code runs the business logic. Documented below.
- Built-in tools — pick a handler from qlaud’s curated catalog (web search, image gen, send email, Slack/Linear/Zendesk/GitHub/Notion actions, code execution), supply your provider API key, no webhook to host. See /v1/builtins.
- MCP servers — connect any Model Context Protocol server URL (Linear, Stripe, Atlassian, Sentry, your own) and we surface every tool it exposes. Zero wrappers to write — vendors already wrote them. See /v1/mcp-servers.
tool_use block, qlaud dispatches it
(webhook POST or in-process handler), awaits the result, appends a
tool_result, re-calls the assistant, and loops until a non-tool-use
turn — same behaviour either flavour.
Kills the per-app tool-call state machine. You write one HTTP handler per
custom tool; qlaud owns the rest.
POST /v1/tools — Register
Body
Response (201)
GET /v1/tools — List
secret is not included.
DELETE /v1/tools/:id — Revoke
How registered webhooks reach the model
Once you’ve registered a webhook tool withPOST /v1/tools, getting it
in front of the model takes one of two shapes — pick based on whether
you want auto-discovery or explicit listing.
Recommended: dynamic discovery (default)
Send your thread message with notools array. tools_mode
defaults to "dynamic" and qlaud injects 4 meta-tools. The model then
calls qlaud_search_tools(intent: "...") and every webhook you’ve
registered appears in the results — alongside built-ins and catalog
MCP connectors. The search is over the full tools table for your
account; there’s no kind filter.
qlaud_search_tools({intent: "current weather"}),
your weather webhook will be in the results, and the model will
invoke it through qlaud_multi_execute. qlaud POSTs the tool input to
your webhook_url, your endpoint returns the JSON, qlaud streams the
result back into the same SSE.
Explicit: pin a fixed list
If you want only specific tools available (no auto-discovery), passtools: ["tool_xxx", ...] with the tool IDs from
POST /v1/tools. tools_mode defaults to "explicit" in this case;
no meta-tools are injected.
Streaming works for either
stream: true flows the dispatch loop through a single SSE
connection regardless of tool kind — webhook, builtin, or MCP all
emit the same qlaud.tool_dispatch_start / qlaud.tool_dispatch_done
events. See the
Streaming section in /api-reference/threads
for the full event vocabulary and a worked example.
Model support
The cross-shape SSE bridge translates each upstream’s native
streaming format (Anthropic
content_block_delta passthrough,
OpenAI delta.tool_calls[].function.arguments chunks → equivalent
Anthropic events) so the qlaud.tool_dispatch_* event vocabulary
fires identically across providers. Vertex’s native Gemini SSE
shape is a small follow-up; until then, route Gemini through the
AI Studio OpenAI-compat endpoint (the default) for full streaming
- tools support.
Webhook contract
When the assistant emits atool_use block, qlaud POSTs the following
payload to your webhook_url:
Headers
Body
Expected response
output can be a string or any JSON value (objects/arrays get stringified
before going back to the assistant). To signal a non-fatal error so the
model can decide what to do:
Verifying the signature
Loop semantics
- Iteration cap: 8 by default. Hitting it returns a partial conversation with
stop_reason: "tool_loop_limit". - Parallel dispatch: when the model emits multiple
tool_useblocks in one turn, qlaud dispatches all of them in parallel viaPromise.all. Total latency = max(per-webhook), not sum. - Retries: 3 attempts with exponential backoff (250 ms / 1 s / 4 s) on 5xx + network errors. 4xx terminates immediately and the result is sent back to the assistant as
is_error: trueso it can decide how to proceed. - Webhook timeout: 30 s default,
timeout_msper-tool override. - Cross-provider: same Anthropic-shape
tool_use/tool_resultsemantics whether the underlying model is Claude or GPT or DeepSeek. You write one handler.
Streaming + tools
POST /v1/threads/:id/messages supports stream: true together with
tools. qlaud opens one upstream call per iteration of the dispatch
loop, tees the response, pipes the customer-facing branch through
verbatim, and inspects the other branch for tool_use blocks. Between
iterations qlaud injects extra SSE events so the UI can render tool
progress inline.
SSE events seen by the customer:
Standard Anthropic events flow as-is during each iteration
(message_start, content_block_start, content_block_delta,
content_block_stop, message_delta, message_stop). Block indexes
reset on every message_start — match tool dispatches by
tool_use_id, not by index.
qlaud-injected events around each tool dispatch:
stream: true with tools — drop stream: true to use the
non-streaming dispatch loop.