# Actions: answers from your own system — Keyda Business docs

- URL: https://keyda.in/business/docs/guide-actions
- Last updated: 2026-10-01

## Actions: answers from your own system

An action lets your bot look something up in your own system in the middle of a chat — where an order is, when it will be delivered, whether a booking is confirmed. The customer asks, the bot collects what it needs, calls your API, and answers from what comes back, in the customer's language.

It works everywhere your bot does: the website widget, your bot link and QR code, and the Android, iOS, React Native, Flutter and Ionic apps. Nothing is installed or updated — the call is made from our servers, so your API credentials never reach a browser or a phone.

### Before you start: anyone can chat with your bot

Your bot is a public chat. If an order number alone is enough to see an order, anyone can type numbers until one works.

> Ask for two details only the real customer knows — the order number **and** the email or phone on the order — and make your API return the order only when both match. Answer "not found" otherwise.

The bot helps in three ways, but none of them replaces that check: it sends only values the customer actually typed, it limits how many lookups one chat or one address can make, and you can choose which fields of the response it is allowed to see.

### Set one up

Open **Actions** in the dashboard and choose **New action**. Owners and admins can see this screen.

- **Name** — for you, such as "Order status".
- **When should the bot use it?** — one sentence, as you would tell a new colleague: "The customer asks where their order is or when it will arrive."
- **Your API address** — `GET` or `POST`, and an `https://` address. Write `{{order_id}}` where a value goes.
- **What the bot asks the customer for** — up to six values. Each has a label in your words ("Order number"), the name your API uses (`order_id`) and a type: any text, a number, an email address or a phone number.
- **Headers** — your API key or token. Stored encrypted and never shown again.
- **Only share these fields with the bot** — optional. List the parts of the response the bot may use, such as `status, eta, items[].name`. Everything else is dropped before the AI sees it.

Press **Test**, type sample values, and you see the request that was sent and exactly what the bot would be given. Then try it for real on the **Test** screen: "Where is my order 48213? My email is asha@example.com".

### What your API receives

With `GET`, values you placed in the address are filled in and the rest are added to the query string:

```
GET https://api.yourshop.com/orders/48213?email=asha%40example.com
```

With `POST`, the values are the JSON body:

```
POST https://api.yourshop.com/lookup
Content-Type: application/json

{"order_id":"48213","email":"asha@example.com"}
```

Every request also carries your own headers and these:

| Header | What it is |
| --- | --- |
| `webhook-id` | A unique id for this request |
| `webhook-timestamp` | When it was sent, in seconds |
| `webhook-signature` | The signature — see below |
| `X-Keyda-Bot` | Your bot's Client ID |
| `X-Keyda-Action` | The action's key, such as `order_status` |
| `X-Keyda-Conversation` | The chat it came from |

### What to send back

Reply within 8 seconds with JSON, or a short line of plain text.

- **200** with the data — the bot answers from it. Keep it small and use clear field names: `{"status":"shipped","eta":"2 October"}` works better than internal codes.
- **404** when nothing matches — the bot tells the customer and asks them to check what they typed.
- Anything else, or no reply in time — the bot apologises, offers your contact details, and the chat is flagged for you. The reason is shown on the Actions screen.

Redirects are not followed, and a response over 256 KB is not read.

### Check the signature

Requests are signed the [Standard Webhooks](https://www.standardwebhooks.com/) way, so you can use an existing library. Your signing secret is on the Actions screen.

By hand: join the id, the timestamp and the exact body with full stops, sign that with HMAC-SHA256 using the secret (the part after `whsec_`, base64-decoded), and compare.

```js
const crypto = require('crypto');

function isFromKeyda(req, rawBody, secret) {
  const id = req.headers['webhook-id'];
  const ts = req.headers['webhook-timestamp'];
  const given = String(req.headers['webhook-signature'] || '').replace(/^v1,/, '');
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // older than 5 minutes
  const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
  const want = crypto.createHmac('sha256', key).update(`${id}.${ts}.${rawBody}`).digest('base64');
  return given.length === want.length
    && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(want));
}
```

For a `GET` request the body is empty, so the signed text ends with a full stop. Use `POST` if you want the values themselves covered by the signature.

### How the bot uses it

- It asks for anything that is missing, one short question at a time, and remembers what the customer said earlier in the chat.
- It never guesses a value. If the customer did not type it, the bot asks.
- One lookup per message. Your saved answers still come first, so a question you have answered by hand never calls your API.
- Answers from a lookup are never reused for another customer.
- A lookup counts as one answer, like any other reply the AI writes.

In **Chats**, an answer that came from a lookup says so underneath: which action ran and how it ended. The values the customer typed stay in the transcript and nowhere else.

### Limits

> 8 actions per bot, 6 values and 8 headers per action, 8 seconds per call. One chat can run 6 lookups in ten minutes and one internet address 20 an hour; your API receives at most 120 calls a minute from your bot.

Actions read information. Do not point one at an address that changes something — cancelling an order or issuing a refund needs a confirmation step the bot does not have yet.
