Keyda Business
Australia Sign in Get started free
Keyda Business docsGuides

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:

HeaderWhat it is
webhook-idA unique id for this request
webhook-timestampWhen it was sent, in seconds
webhook-signatureThe signature — see below
X-Keyda-BotYour bot's Client ID
X-Keyda-ActionThe action's key, such as order_status
X-Keyda-ConversationThe 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 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.

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.

Next: Widget API →