Build

Tools

A tool is an HTTP endpoint of yours that an agent can call. With tools an agent can look up an order, check a calendar or book a slot while it is still on the line.

Two kinds of tool

KindWhen it runsUse it for
during_callWhen the model decides to, in the middle of the conversationLooking something up or doing something the caller has just asked for.
before_callOnce, as the call startsFetching what the agent should know before it says hello: the caller’s profile, open orders, account status.

Create a tool

A tool belongs to one agent. Name it as you would a function; the name is how the model asks for it. The description is what the model reads to decide when to use it, so say plainly what it does and when.

curl -X POST "https://goblins-call-api.onrender.com/v1/tools" \
  -H "Authorization: Bearer $CRACKLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "agentId": "ua_3f9a1c2b",
  "name": "check_order_status",
  "description": "Looks up an order by its number, which the caller reads out. Use it when the caller asks where their order is.",
  "kind": "during_call",
  "method": "GET",
  "url": "https://your-app.com/api/orders/{{order_id}}",
  "params": [
    {
      "key": "order_id",
      "value": "A-1042"
    }
  ],
  "auth": "bearer",
  "authToken": "YOUR_ENDPOINT_TOKEN",
  "timeoutMs": 5000
}'

Then switch it on for the agent: add the tool’s ID to the agent’s toolIds, or mention it in the instructions as @check_order_status. Either is enough.

How arguments reach your endpoint

Each entry in params is an argument the model fills in from the conversation. Its value is an example, shown to the model so it knows what the argument looks like; it is also what is sent if the model leaves the argument out. Say what each argument means in the tool’s description. When the tool runs:

  • {{order_id}} in the URL, in a header or in the body template is replaced with the argument.
  • For a GET, every param is also added to the query string.
  • For a POST with no body template, the params are sent as a JSON object. With a template, the template is sent, with its placeholders filled in.

An endpoint for the tool above could look like this:

Your endpoint
import express from 'express';

const app = express();

// GET /api/orders/A-1042, called by the agent in the middle of a call
app.get('/api/orders/:orderId', async (req, res) => {
  if (req.get('authorization') !== `Bearer ${process.env.TOOL_TOKEN}`) return res.sendStatus(401);

  // A test call from the studio has no real caller: answer with sample data and save nothing.
  if (req.get('x-goblin-test-call') === 'true') {
    return res.json({ status: 'out for delivery', eta: 'today, 4 to 6 pm' });
  }

  const order = await findOrder(req.params.orderId);
  if (!order) return res.json({ found: false });
  res.json({ found: true, status: order.status, eta: order.eta });
});

What to send back

Answer with JSON. The model reads your response and carries on its reply from it, so return the few facts worth saying and name them clearly. The model is shown about the first 1,800 characters; anything beyond that is cut off.

The caller is waiting while your endpoint works. The agent says a short holding line, such as “one second, let me check”, and then runs the tool. A request is given timeoutMs to answer, 30 seconds at the very most. If it fails or runs out of time, the agent tells the caller it could not check just now and carries on; it does not make up a result.

Authenticating to your endpoint

`auth`What is sent
noneNothing extra.
bearerAuthorization: Bearer <authToken>
api_key<authToken> in the header named by authKeyHeader (X-API-Key unless set).
basicAuthorization: Basic … built from authUsername and authToken.

Credentials are stored encrypted and are never shown to the model. Tool URLs must be public http(s) addresses, and redirects are not followed.

Trusting what reaches your endpoint

Arguments written by the model come from what the caller said, so treat them as you would anything typed into a form. To know for certain which call a request belongs to, put {{call_id}} in the URL, a header or the body. It is filled in from the call itself, as are {{agent_id}} and the variables the call was started with, and none of them can be overridden by the model.

A tool that identifies the caller from the call
{
  "name": "get_my_appointments",
  "description": "Lists the caller's upcoming appointments.",
  "method": "GET",
  "url": "https://your-app.com/api/appointments?call={{call_id}}&customer={{customer_id}}"
}

Test calls

A request that is not made for a real caller carries the header X-Goblin-Test-Call: true. That covers test calls started in the studio and Test a tool. Such a call has none of the variables your server sends with a real one, so an endpoint that needs to know who is calling can answer with sample data and save nothing. Only the platform can set this header.

Before-call tools

A before_call tool runs once when the call is started, with the call’s variables as its arguments. Whatever it returns is added to the agent’s instructions for that call, so the agent already knows it when it greets the caller. The call is not set up until these tools have answered, so keep them quick.

Built-in tools

Two actions need no endpoint. Switch them on with the agent’s systemTools:

endCallboolean

The agent may hang up once the conversation is finished.

changeLanguageboolean

The agent may switch the language it speaks between Hindi and English when the caller does.

curl -X PATCH "https://goblins-call-api.onrender.com/v1/agents/ua_3f9a1c2b" \
  -H "Authorization: Bearer $CRACKLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "systemTools": {
    "endCall": true,
    "changeLanguage": true
  }
}'