Skip to main content
Tools are functions the assistant can call mid-conversation. Register a tool once with the URL of an HTTP endpoint you control, then reference it by ID from /v1/threads/:id/messages. When the assistant emits a tool_use block, qlaud POSTs to your webhook, awaits the result, appends a tool_result, re-calls the assistant, and loops until a non-tool-use turn. Kills the per-app tool-call state machine. You write one HTTP handler per tool; qlaud owns the rest.

POST /v1/tools — Register

curl https://api.qlaud.ai/v1/tools \
  -H "x-api-key: $QLAUD_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "name": "get_weather",
    "description": "Get current weather for a location",
    "input_schema": {
      "type": "object",
      "properties": {"location": {"type": "string"}},
      "required": ["location"]
    },
    "webhook_url": "https://my-app.example/qlaud/tools/weather",
    "timeout_ms": 15000
  }'

Body

FieldTypeRequiredDefaultDescription
namestringyesThe function name passed to the LLM. Per-account unique among non-revoked tools.
descriptionstringyesForwarded to the LLM as the tool description.
input_schemaJSON SchemayesForwarded as input_schema.
webhook_urlstringyesMust be https://. qlaud POSTs the tool_use payload here.
timeout_msnumberno30000Per-tool override of the 30 s default. Max 120000.

Response (201)

{
  "id": "tool_f5d6afcef39743bcbb3114ce3b9c8e66",
  "object": "tool",
  "name": "get_weather",
  "description": "Get current weather for a location",
  "input_schema": { "type": "object", "...": "..." },
  "webhook_url": "https://my-app.example/qlaud/tools/weather",
  "timeout_ms": 15000,
  "secret": "wsk_AbCdEf...XyZ",
  "created_at": 1777262997717
}
secret is returned once. You use it to verify the HMAC-SHA256 signature on every webhook delivery. Lose it and you’ll need to revoke + re-register the tool to get a new one.

GET /v1/tools — List

curl https://api.qlaud.ai/v1/tools -H "x-api-key: $QLAUD_API_KEY"
Returns every non-revoked tool you’ve registered. secret is not included.

DELETE /v1/tools/:id — Revoke

curl -X DELETE https://api.qlaud.ai/v1/tools/$TOOL_ID \
  -H "x-api-key: $QLAUD_API_KEY"
Soft revoke. New thread messages can’t reference the tool by id; existing thread audits still resolve cleanly.

Webhook contract

When the assistant emits a tool_use block, qlaud POSTs the following payload to your webhook_url:

Headers

X-Qlaud-Timestamp: 1777262997717
X-Qlaud-Signature: <hex hmac-sha256 of "{timestamp}.{body}">
X-Qlaud-Tool-Id: tool_f5d6afcef...
X-Qlaud-Request-Id: msg_xxx
content-type: application/json

Body

{
  "tool_id": "tool_f5d6afcef...",
  "tool_use_id": "toolu_xxx",
  "name": "get_weather",
  "input": { "location": "San Francisco" },
  "request_id": "msg_xxx",
  "thread_id": "2f1d0c7f-..."
}

Expected response

{ "output": "It is 72°F sunny in San Francisco" }
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:
{ "output": "rate limit hit", "is_error": true }

Verifying the signature

import hmac, hashlib

def verify(headers, body_bytes, secret):
    ts = headers["X-Qlaud-Timestamp"]
    sig = headers["X-Qlaud-Signature"]
    payload = f"{ts}.{body_bytes.decode()}".encode()
    expected = hmac.new(
        secret.encode(), payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(sig, expected)

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_use blocks in one turn, qlaud dispatches all of them in parallel via Promise.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: true so it can decide how to proceed.
  • Webhook timeout: 30 s default, timeout_ms per-tool override.
  • Cross-provider: same Anthropic-shape tool_use/tool_result semantics whether the underlying model is Claude or GPT or DeepSeek. You write one handler.

Errors

StatusMeaning
400Invalid body (missing required field, non-https:// URL, schema not an object)
409A non-revoked tool with the same name already exists
401Bad / revoked qlk key
404Tool not found OR not owned by caller