Tools & Function Calling

Tools let an agent reach outside its own knowledge and take action — look up a live order status, query your database, trigger a workflow — during a single chat turn. You register an HTTPS endpoint as a tool; the model decides when it needs to call it; PulseLABS executes the call and feeds the result back before producing the final answer.

This is standard function calling: you describe what the tool does and what arguments it takes with a JSON Schema, and the underlying LLM decides for itself whether and how to call it.

Tools are workspace-scoped, like knowledge sources. One tool can be linked to several agents; an agent only calls tools explicitly linked to it, and only when tools.enabled: true is passed on the chat call.

How it works

  1. Register a tool: a name, a description (shown to the model), a JSON Schema for its arguments, and the HTTPS URL PulseLABS should call.
  2. Link the tool to one or more agents.
  3. Enable tools on a chat call. PulseLABS converts each linked tool into an OpenAI-style function definition and passes it to the model alongside the conversation.
  4. The model decides. If it determines a tool call would help answer the message, it returns a tool call request instead of (or before) a final answer.
  5. PulseLABS executes the call: an HMAC-signed HTTPS POST to your registered URL, with a 15-second timeout.
  6. The result is fed back to the model as part of the conversation, and a new completion is requested. This loop runs for up to 3 rounds before PulseLABS forces a final answer, so an agent can chain several tool calls in one turn.

Registering a Tool

Register an HTTP endpoint as a callable tool. Agents with this tool linked can request it during chat; PulseLABS executes the call (HMAC-signed, like webhooks) and feeds the result back to the model.

Request body
NameTypeRequiredDescription
namestringrequiredFunction name exposed to the model. Must be a valid identifier (e.g. get_order_status).
descriptionstringrequiredWhat the tool does and when to call it — this is what the model reads to decide whether to use it. Be specific.
parametersobjectrequiredJSON Schema object describing the tool's arguments.
urlstringrequiredHTTPS endpoint invoked when the tool is called.
agentIdsstring[]optionalAgent persona IDs to link this tool to at creation time.
Response
{
  "tool": {
    "id": "tool_xxx",
    "name": "get_order_status",
    "description": "Look up the current status of a customer order by order ID.",
    "parameters": { "...": "..." },
    "url": "https://yourapp.com/webhooks/tools/order-status",
    "isActive": true,
    "createdAt": "2026-06-20T10:00:00Z"
  },
  "secret": "toolsec_3f9a8c..."   // shown once — store it to verify incoming calls
}
curl -X POST https://api.pulsesoftwareapp.com/v1/tools \
  -H "x-api-key: sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "get_order_status",
    "description": "Look up the current status of a customer order by order ID.",
    "parameters": {
      "type": "object",
      "properties": {
        "orderId": { "type": "string", "description": "The order ID to look up" }
      },
      "required": ["orderId"]
    },
    "url": "https://yourapp.com/webhooks/tools/order-status",
    "agentIds": ["agent_xxx"]
  }'
The secret is only ever returned in the create response. GET and PUT responses always omit it. If you lose it, delete the tool and register it again.

List all tools registered in the workspace.

Response
{
  "tools": [
    {
      "id": "tool_xxx",
      "name": "get_order_status",
      "isActive": true,
      "agents": [{ "id": "agent_xxx", "name": "Victoria Chen" }]
    }
  ]
}

Get a single tool's definition and linked agents.

Path / Query parameters
NameTypeRequiredDescription
idstringrequiredTool ID
Response
{
  "tool": {
    "id": "tool_xxx",
    "name": "get_order_status",
    "description": "Look up the current status of a customer order by order ID.",
    "parameters": { "...": "..." },
    "url": "https://yourapp.com/webhooks/tools/order-status",
    "isActive": true,
    "agents": [{ "id": "agent_xxx", "name": "Victoria Chen" }]
  }
}

Update a tool's name, description, parameters, URL, or active state.

Path / Query parameters
NameTypeRequiredDescription
idstringrequiredTool ID
Request body
NameTypeRequiredDescription
namestringoptionalNew function name.
descriptionstringoptionalNew description shown to the model.
parametersobjectoptionalNew JSON Schema for arguments.
urlstringoptionalNew HTTPS endpoint.
isActivebooleanoptionalSet false to disable the tool without deleting it — agents will stop being able to call it.
Response
{ "tool": { "id": "tool_xxx", "isActive": false, "...": "..." } }

Permanently delete a tool.

Path / Query parameters
NameTypeRequiredDescription
idstringrequiredTool ID
Response
// 204 No Content

Linking Tools to Agents

Link at registration time via agentIds, or manage links afterwards with these endpoints — useful for giving the same tool to a new agent or revoking access without deleting the tool.

Link a tool to an agent.

Path / Query parameters
NameTypeRequiredDescription
idstringrequiredAgent ID
toolIdstringrequiredTool ID
Response
{
  "agent": {
    "id": "agent_xxx",
    "tools": [{ "id": "tool_xxx", "name": "get_order_status" }]
  }
}

Unlink a tool from an agent. The tool itself is not deleted.

Path / Query parameters
NameTypeRequiredDescription
idstringrequiredAgent ID
toolIdstringrequiredTool ID
Response
// 204 No Content

Verifying Tool Calls

Every tool invocation is an HTTPS POST to your registered URL, signed the same way as PulseLABS webhooks. Verify the signature before trusting the payload — this is what proves the request came from PulseLABS and not an impersonator who guessed your endpoint.

Request sent to your tool's URL:

POST https://yourapp.com/webhooks/tools/order-status
Content-Type: application/json
X-PulseLabs-Tool: get_order_status
X-PulseLabs-Signature: sha256=<hmac-sha256 of the raw body, keyed with your tool's secret>
User-Agent: PulseLabs-Tools/1.0

{
  "tool": "get_order_status",
  "arguments": { "orderId": "4471" }
}

Your endpoint has up to 15 seconds to respond. Respond with 2xx and a JSON body — that body becomes the tool's result and is handed straight back to the model. Any non-2xx status or timeout is reported to the model as a tool error so it can recover gracefully.

import crypto from 'crypto'

function verifyToolSignature(rawBody, signatureHeader, secret) {
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))
}

// In your endpoint handler:
app.post('/webhooks/tools/order-status', (req, res) => {
  const signature = req.headers['x-pulselabs-signature']
  if (!verifyToolSignature(req.rawBody, signature, process.env.TOOL_SECRET)) {
    return res.status(401).send('invalid signature')
  }
  const { tool, arguments: args } = req.body
  // ... look up args.orderId and respond
  res.json({ status: 'shipped', eta: '2026-06-28' })
})

Calling Tools During Chat

Tool calling is opt-in per call — pass tools.enabled: true on a normal Agent Chat request. The model itself decides whether the message warrants a tool call; if you only want some of an agent's linked tools available for this turn, restrict with toolIds.

Request fields:

"tools": {
  "enabled": true,            // default false
  "toolIds": ["tool_xxx"]     // optional — restrict to a subset of the agent's linked tools
}

The response includes a toolCalls trace of every invocation made while answering this turn — useful for debugging, billing, or showing users what the agent did:

"toolCalls": [
  {
    "toolName": "get_order_status",
    "arguments": { "orderId": "4471" },
    "result": { "status": "shipped", "eta": "2026-06-28" },
    "success": true,
    "durationMs": 312
  }
]
Up to 3 rounds of tool calls run automatically per chat turn (MAX_TOOL_ROUNDS). If your use case needs an agent to chain more than three dependent lookups in a single turn, split the work across two chat calls instead of relying on a higher round limit.
curl -X POST https://api.pulsesoftwareapp.com/v1/agents/agent_xxx/chat \
  -H "x-api-key: sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "What is the status of order #4471?",
    "tools": { "enabled": true }
  }'