Skip to content
dhanu docs
Browse pages

Guides

Build a support bot on a staff member

Give a staff member your support inbox, trigger it from your backend for every ticket, read the reply back, and post it where your customers are.

A staff member is an AI colleague with a name, a brief and the workspace’s knowledge behind it. Handing it a support ticket is one request; reading its answer back is one more. This guide wires that into a backend that receives tickets from anywhere, a help-desk webhook, a form, a chat app.

1. Set up the staff member

In the app, create a staff member (Team → Staff) and write its brief: what your product is, how to answer, when to escalate. Add the pages it should know to the brain. On its Channels tab, switch on the API channel, that mints a trigger address like stf_a1b2c3 that you will paste into your backend.

2. Mint a key

In the developer console, create a key scoped to the tasks package with the test mode while you build (it answers with fixtures and spends nothing), then a live one when you go to production. Keep it in DHANU_API_KEY.

3. Trigger the staff member for each ticket

Trigger with the ticket text as input and the ticket id as threadId, so a follow-up from the same customer continues the same task instead of starting a new one. Pass the ticket id as idempotencyKey too, so a retried webhook cannot create two tasks.

import { Dhanu } from "dhanu";

const dhanu = new Dhanu(process.env.DHANU_API_KEY!);

export async function handleTicket(ticket: { id: string; subject: string; body: string }) {
  const task = await dhanu.tasks.trigger({
    address: process.env.SUPPORT_STAFF_ADDRESS!,
    input: ticket.body,
    subject: ticket.subject,
    threadId: ticket.id,
    idempotencyKey: `ticket-${ticket.id}`,
  });
  return task.id;
}
import os
from dhanu import Dhanu

dhanu = Dhanu(os.environ["DHANU_API_KEY"])

def handle_ticket(ticket):
    task = dhanu.tasks.trigger(
        address=os.environ["SUPPORT_STAFF_ADDRESS"],
        input=ticket["body"],
        subject=ticket["subject"],
        thread_id=ticket["id"],
        idempotency_key="ticket-" + ticket["id"],
    )
    return task.id

4. Read the reply back

The trigger returns as soon as the task exists; the staff member answers in the background. Two ways to get the answer:

Stream it: hold a connection until the reply lands. Good for a chat widget or a worker that handles one ticket at a time.

for await (const message of dhanu.tasks.stream(taskId)) {
  await helpdesk.reply(ticket.id, message.text);
}
for message in dhanu.tasks.stream(task_id):
    helpdesk.reply(ticket["id"], message.text)

Subscribe to the webhook: register an endpoint for task.message.created (see receive webhooks); each delivery carries the task id and the message, so a stateless backend can post the reply without holding anything open.

5. Keep it safe

Everything you send is treated as untrusted text: the staff member reports on a ticket that tells it to “ignore your instructions”, it never obeys it. The run is non-interactive, so it can reach the brain and its tools but never a person’s local folders or browser. Scope the key to tasks.trigger and tasks.read so a leaked key can hand the bot work but nothing else, and set a monthly spend cap on it.