Skip to main content
Every agent works out of a toolbox. Its environment supplies the core of it (a browser environment brings navigation, clicking, typing) and the agent layers its own built-in tools on top. Custom tools are the part you add: functions from your own code that complement that toolbox with anything it can’t reach on its own, like querying your database, calling an internal API, or looking up a customer record. Pass a function to the SDK and the agent uses it like any other tool: when it decides to call one mid-run, the SDK executes your function locally and the run continues with the result. The full loop is handled for you.

With the SDKs

Pass your functions via tools; the schema is derived from the signature and docstring in Python, or declared with tool() in TypeScript.
from hai_agents import Client

def get_order_status(order_id: str) -> str:
    """Look up the status of an order in our system."""
    return db.orders.get(order_id).status

client = Client()
result = client.run_session(
    agent="h/web-surfer-flash",
    messages="Check order 4242 and email the customer if it shipped.",
    tools=[get_order_status],
)
print(result.answer)
Functions may be sync or async, and exceptions are reported to the agent as tool errors instead of crashing the run. Tools execute in the process that polls the session, so they only run while your program is waiting on run_session / runSession (or a handle’s wait_for_completion / waitForCompletion with the same tools). Execution is at-least-once: if posting a result fails and the wait is retried, the tool may run again, so prefer idempotent tool functions for side-effecting operations.

Over the raw API

Declare the tools when creating the session, inline on the agent or via the agent.tools override for a registered agent:
{
  "agent": {
    "name": "support-agent",
    "environments": [{ "kind": "web" }],
    "tools": [
      {
        "name": "get_order_status",
        "description": "Look up the status of an order in our system.",
        "input_schema": {
          "type": "object",
          "properties": { "order_id": { "type": "string" } },
          "required": ["order_id"]
        }
      }
    ]
  },
  "messages": "Check order 4242 and email the customer if it shipped."
}
Long-poll changes for an ActiveStateChangeEvent with state: "awaiting_tool_results"; its pending_tool_calls lists each call as a tool_req (tool_name, args, id). Execute and post the result, echoing the tool_req back:
curl -X POST "https://agp.eu.hcompany.ai/api/v2/sessions/$SESSION_ID/tool_results" \
  -H "Authorization: Bearer $HAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind": "tool_result", "tool_req": {"tool_name": "lookup_order", "args": {"order_id": "4242"}, "id": "call_1"}, "result": "shipped"}'
Send several at once with {"type": "batch", "results": [...]}, and report a failure as an error_event instead of a tool_result. The agent resumes once every pending call has a result; calls still unresolved when the run ends (for example on max_time_s) fail with a model-visible error. Posting to a finished session returns 409.