Function steps leverage the Yext Functions framework to give Yext Chat access to user-defined serverless Typescript functions. This allows the bot to perform complex tasks like:
- Transforming, reading, and writing data to external APIs
- Using open-source libraries to manipulate or analyze data
- Performing complex business logic
The function has access to the full history of the conversation, including any collected data, as well as any additional context data you’ve provided.
Setting up a Function Instruction
To set up this instruction, you will need to, first, make sure you have a plugin created ahead of time. This is where you will reference the functions that your bot will utilize. Read more in this guide .
Once you are in the Add/Edit an Instruction modal, you have the following inputs to complete:
- Instruction: A description of what you want the bot to do.
- Plugin: A Plugin is a resource that contains at least one Function. Plugins can be configured on an account using the Yext CLI.
- Function: Select the function you want to use from your Plugin.
Example
The function instruction can be used for things like sending data that was collected in a previous Collect instruction to a third party.
Going back to the “Get a Demo” goal example from previous units, we can use the personal data gathered from the bot and send it to Marketo so they can be contacted via email by a marketing or sales team.
First, there needs to be a collect instruction to gather data like the user’s email address, name, and any other relevant information. Then you can set up a function instruction to send the field values to Marketo using a plugin configured in your account.

JSON Configuration
The function has access to the full history of the conversation, including any collected data, as well as any additional context data you’ve provided.
Additionally, the return value of the function is saved into the queryResult, so the bot can use it to answer questions.
Let’s walk through an example.
Example
In this example, we’ll show a function that uses OpenWeatherMap to fetch the weather in a specific city.
To use a function in an instruction, simply specify the pluginId (a plugin is a collection of functions) and the functionName.
{
"$id": "example",
"$schema": "https://schema.yext.com/config/chat/chat-bot/v1",
"name": "Example",
"initialMessage": "Hello! How are you doing?",
"goals": {
"GET-WEATHER": {
"goal": "Get the weather in a city.",
"examples": [
"How's the weather in San Francisco?",
"What's the weather in Los Angeles?",
"What's the weather in San Diego?"
],
"instructions": [
{
"collect": {
"instruction": "Ask the user what city they're interested in",
"fields": [
{
"fieldType": "STRING",
"id": "city",
"optional": false
}
]
}
},
{
"function": {
"functionName": "getWeather",
"pluginId": "weatherPlugin"
}
},
{
"reply": {
"instruction": "Based on the response, tell them the weather in that city. You must ALWAYS respond in Farenheit. You may convert the temperature from Kelvin/Celsius to Farenheit if needed.",
"mode": "DIRECT_ANSWER"
}
}
]
}
}
}So what should the function actually look like? Yext functions receive a payload object with the following keys:
-
messages- the message history -
notes- the “notes” stored in the conversation, which includes thequeryResultfrom previous messages, thepredictedGoal, and more -
context- any additional metadata passed to the Chat API, which might include details about the user or other contextual information that might influence the chat bot
(In other words, the Yext function has access to the exact same data that the Chat API has access to.)
Therefore, a function to fetch data based on the “city” collected from the user might look like this:
export const getWeather = async ({ messages, notes }) => {
const city = notes.collectedData.city;
if (!city) {
throw new Error("No city provided".)
}
const params = {
city,
apiKey: "<YOUR_API_KEY_HERE>",
};
const url = `https://api.openweathermap.org/data/2.5/weather?q=${params.city}&appid=${params.apiKey}`;
const response = await fetch(url);
const data = await response.json();
return {
queryResult: data,
};
};In this function, we first confirm that a city was collected (which should always be the case, because the function step is preceded by a collect step that prompts the user for a city.
Next, we send an API request to an API and put its response in the queryResult portion of the response, which makes it available to the next step in the instructions.
Typing the Function
Yext functions use the Deno runtime, which supports Typescript out of the box. If you want Typescript typings for your Chat serverless functions, you can use the types below, which are written using Zod, so that you can use them for runtime validation as well.
import { z } from "https://deno.land/x/zod/mod.ts";
const ChatFunctionPayloadSchema = z.object({
messages: z.array(
z.object({
text: z.string(),
timestamp: z.string(),
source: z.enum(["USER", "BOT"]),
})
),
notes: z
.object({
currentGoal: z.string().optional(),
currentStepIndices: z.array(z.number()).optional(),
searchQuery: z.string().optional(),
queryResult: z.any(),
collectedData: z.record(z.string()).optional(),
goalFirstMsgIndex: z.number().optional(),
})
.optional(),
});
export type ChatFunctionPayload = z.infer<typeof ChatFunctionPayloadSchema>;
export type ChatFunctionReturn = Partial<ChatFunctionPayload["notes"]>;
export type ChatFunction = (
payload: ChatFunctionPayload
) => Promise<ChatFunctionReturn>;