Cookbooks · Agents that do things
Greet callers by name
An agent that opens with “Hi Divya, are you calling about the aligner set arriving Thursday?” instead of “Can I have your account number?”. Your server says who is calling, and a tool that runs before the call fetches what the agent should know about them.
Before you start
- An agent, and its ID. The Quickstart makes one.
- A workspace API key in the
CRACKLE_API_KEYenvironment variable. See Authentication. - Node.js 18 or later, with
"type": "module"inpackage.json, andnpm install express. - A token of your own choosing in
TOOL_TOKEN.
How it fits together
- Your server starts the call and passes one variable: your ID for the person calling.
- Before the agent is sent to the room, Crackle calls your
before_calltool with that ID. - Whatever your endpoint returns is added to the agent’s instructions for this one call. The agent greets the caller already knowing it.
Write the lookup endpoint
tools-server.jsimport express from 'express'; const app = express(); const customers = new Map([ ['cus_812', { name: 'Divya', plan: 'Family', openOrder: 'Aligner set, arriving Thursday' }], ]); app.get('/tools/customer', (req, res) => { if (req.get('authorization') !== `Bearer ${process.env.TOOL_TOKEN}`) return res.sendStatus(401); // A test call from the studio has no customer behind it: give the agent someone to talk to. if (req.get('x-goblin-test-call') === 'true') { return res.json({ known: true, name: 'Sam (test caller)', plan: 'Family', openOrder: null }); } const customer = customers.get(req.query.id); res.json(customer ? { known: true, ...customer } : { known: false }); }); app.listen(3000);Create the agent and the tool
The tool’s
kindisbefore_call, and its URL uses the variable. The instructions tell the agent what the record is and what to do when there is none.setup.jsconst API = 'https://goblins-call-api.onrender.com'; const headers = { Authorization: `Bearer ${process.env.CRACKLE_API_KEY}`, 'Content-Type': 'application/json', }; async function api(method, path, body) { const response = await fetch(`${API}${path}`, { method, headers, body: body && JSON.stringify(body) }); const data = await response.json(); if (!response.ok) throw new Error(`${method} ${path}: ${data.error}`); return data; } const { agent } = await api('POST', '/v1/agents', { name: 'Sunrise Dental support', systemInstruction: [ 'You answer support calls for Sunrise Dental.', 'Before the call you are given the record of the person calling, from @get_customer.', 'If it says known is true, use their name and what you know about their plan and open order without asking for it.', 'If it says known is false, or there is no record, ask for their name and carry on.', 'Keep every reply to one or two short sentences.', ].join('\n'), greetingInstruction: 'Greet the caller by name if you know it, and ask how you can help.', voice: { language: 'en' }, // The blank each call fills in. An empty default means "nobody we know". variables: { customer_id: { defaultValue: '', description: 'Your ID for the person calling' } }, }); await api('POST', '/v1/tools', { agentId: agent.id, name: 'get_customer', description: 'The record of the person calling: their name, plan and any open order.', kind: 'before_call', method: 'GET', url: `${process.env.PUBLIC_URL}/tools/customer?id={{customer_id}}`, auth: 'bearer', authToken: process.env.TOOL_TOKEN, timeoutMs: 3000, }); console.log('Agent ready:', agent.id);Start calls with the caller's ID
Wherever your server starts a call, it now says who the call is for:
curl -X POST "https://goblins-call-api.onrender.com/v1/calls" \ -H "Authorization: Bearer $CRACKLE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "YOUR_AGENT_ID", "participant": { "identity": "cus_812" }, "variables": { "customer_id": "cus_812" } }'
Try it
Start a call with customer_id set to cus_812 and join it: the agent greets Divya and knows about her order. Start one with an ID your server does not know and it asks for a name instead. Test agent in the studio greets the test caller, because the endpoint recognises a test call.
Things to know
- The lookup happens while the call is being started, so the request to start it takes as long as your endpoint does. Keep the endpoint quick and its timeout short.
- If your endpoint fails or times out, the call still starts. The agent is told the lookup failed, which is why the instructions say what to do without a record.
- Return only what the agent should use. Everything in the response is put in front of the model, so leave out internal notes and anything the caller should not hear back.
- For something that can change during the call, such as a live order status, add a
during_calltool as well: see An agent that books appointments.