> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentcard.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Acknowledging the shopper: a reaction or a short text

> Tell the shopper you are on it while Agentcard works, with a 👀 on their message or a short text.

A first search can take Agentcard most of a minute. While it works, the shopper sees a 👀 on their own message, the way a friend reacts to a text before answering it. In a chat that shows no reactions, the shopper reads a short text instead, such as `on it, looking for usb-c cables under $20`.

## React with a 👀

Pass `onReaction` to `buy.turn`. Show the emoji as a tapback on the shopper's bubble.

```ts chat.ts theme={null}
const turn = buy.turn(message, {
  onReaction: (emoji) => reactToShopperMessage(shopper.id, emoji), // your code; emoji is always '👀'
});
```

A reaction is the default acknowledgement. It names nothing the shopper said, so it never reads a request back wrong, and the chat holds only real answers. Each message gets at most one 👀, and it goes out in these cases:

* A message that starts a search gets it the moment `buy.turn` opens, before your model runs.
* A call that Agentcard has not answered within 1.5 seconds puts the 👀 on a message that has none yet: a slow `yes`, a pick, or a confirm that is still placing the order. The tool sends no text with the 👀.

These messages get none when the turn opens, because Agentcard answers them fast: a pick of a product or a store the shopper saw, an address or other details, an answer to a question such as `yes`, `2`, or a code, a step toward checkout such as `ok lets checkout`, and an acknowledgement such as `its ok`.

When the 👀 went out, the model reads `shopper_already_told_looking: true` in that turn's answers, and its description tells it not to acknowledge the request again. The reply opens with the answer or the question, not `on it`. The field reads false on a turn that reacted with nothing, and when `onReaction` threw. An `onReaction` that throws costs only the reaction.

With `onItem` on the turn, the 👀 still goes to `onReaction` and never joins the turn's items, because it sits on the shopper's bubble and not in the agent's sequence.

## Send a short text instead

For SMS, or any chat that shows no reactions, pass `onWorking` and no `onReaction`, or set `ack: 'text'`:

```ts chat.ts theme={null}
const buy = buyTool({
  agentcard,
  externalUserId: shopper.id,
  ack: 'text',
  onWorking: (text) => textShopper(shopper.id, text), // your code
});
```

`ack` takes `'reaction'`, `'text'`, or `'none'`. Left out, the tool reacts when the tool or the turn has an `onReaction`, and sends text through `onWorking` otherwise. With both callbacks, the shopper gets the 👀 and never the text too. `'none'`, or `false`, sends nothing.

In text mode, the tool sends the reply when Agentcard has not answered a new request within 1.5 seconds, so a quick answer arrives alone. It sends one for the calls the model makes in one turn. The reply waits for `onWorking`, so the shopper reads it before any link and before the model answers. An `onWorking` that throws costs only the instant reply.

| The shopper's message | The text |
| - | - |
| A search | `on it, looking for usb-c cables under $20` or `give me a sec, checking for a table for 2 in north beach` |
| A slow pick of a product | `adding it now` |
| A slow pick of a store or a place in a list, such as `the first one` | `got it, one moment` |
| A slow confirm | `on it, one moment` |
| A code | No text. |

The words rotate through six templates, and one tool never sends the same template twice in a row. A short paraphrase of the request goes in when the tool can read one. It stops before any link, email address, phone number, street address, or long number, and a request that starts with one gets a reply that names nothing, such as `looking for that now`.

Pass `ack: (ask) => text` to write your own text from the shopper's words, where an empty answer sends none. Pass `ackDelayMs` to change the 1.5 seconds for either mode.

## Acknowledge before the model runs

The model takes several seconds to call the tool after the shopper's message arrives. With `buy.turn`, the turn acknowledges the moment it opens, and calls `instantReaction` or `instantAck` for you. Outside a turn, call `buy.instantReaction(message)` the moment the message arrives, before your model runs, and put the emoji it returns on the message. In text mode, call `buy.instantAck(message)` and send the text it returns.

```ts chat.ts theme={null}
const reaction = buy.instantReaction(message); // '👀' or null
if (reaction) await reactToShopperMessage(shopper.id, reaction);
// In text mode instead:
const ack = buy.instantAck(message);
if (ack) await textShopper(shopper.id, ack);
```

Both return null for a message that starts no search, such as a confirmation, a short answer, a step toward paying, a pick of something the shopper saw, a code, small talk like `hi`, a message that carries the shopper's own details, or a short reply right after a question the last answer asked. A reply that opens a new request, such as `find me black running shoes`, or that runs past four words is still a search. When either returns an acknowledgement, the tool sends none of its own for that turn.

The reply never reads the shopper's details back, and it names the request only when it reads as a short, whole phrase. Otherwise it says `on it, give me a moment` or another reply that names nothing. Pass `ack: 'none'` to the tool when you acknowledge yourself.

## Show a long wait

A turn that runs long, even past a minute, gets no text line. A search often takes 10 to 40 seconds, so a line at any wait short enough to help would land on most searches and bring back the chatter the reaction replaces.

To show a long wait, pass `onProgress` and show its stage as a status line or a typing indicator:

```ts chat.ts theme={null}
const turn = buy.turn(message, {
  onProgress: ({ stage }) => showStatus(shopper.id, stage), // 'searching', 'building_cart', 'checking_out' or 'working'
});
```

The tool then asks Agentcard to stream each call. Pass `stream: false` to wait for each whole answer instead.

## Check it in the sandbox

1. Send "two packs of AA batteries from Amazon" as a test shopper. Check that the 👀 appears on your message at once, before the model answers, and that the reply opens with the answer.
2. Send "yes" after a cart. Check that no 👀 appears at once, and that a slow answer gets one after about 1.5 seconds.
3. Send "the first one" after a product list. Check that no reaction or text appears when the turn opens.
4. Switch to `ack: 'text'`. Check that the text names your request or nothing, and that it never repeats an email address or a street address you typed.
5. Make `onReaction` throw. Check that the turn still answers and the model reads `shopper_already_told_looking: false`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.