How to Use the Structured Fetch Ability Action
In this tutorial, you will learn how to use ChatBotKit's structured fetch ability action to make HTTP requests from your bot. You will see three ways to define a fetch action - the simple placeholder format, the structured YAML format, and the YAML tags format - and how to connect secrets for API authentication, both for administrator-managed and end-user-authenticated scenarios.
What You Will Learn
- How to write a fetch ability using the simple format with placeholders
- How to write the same ability using the structured YAML format with typed field tags
- How to write the same ability using YAML tags (
!fetch,!string, etc.) for fully typed, machine-parseable definitions - When to choose one format over the other
- How to authenticate API calls using secrets
- The difference between admin-managed secrets and user-authenticated secrets
Prerequisites
- A ChatBotKit account
- Basic familiarity with bots, skillsets, and abilities (see the Weather Forecast Bot tutorial for an introduction)
The Simple Format
The simple format uses backtick-delimited fetch code blocks inside the ability instruction. The AI model reads the instruction text and fills in any placeholders before executing the request.
Example: Fetching Weather Data
In this format:
- The instruction is free-form text mixed with one or more
fetchcode blocks. - Placeholders like
{location}are filled in by the AI model based on context from the conversation. - You can include natural-language descriptions around the code block to guide the model's behavior.
- The HTTP request inside the code block follows a raw HTTP-like syntax with the method, URL, and headers.
This format works well for simple, public APIs that do not require authentication. The AI model has full flexibility to decide how to construct the request.
The Structured Format
The structured format uses YAML with special field tags to define the request in a more precise, machine-readable way. Instead of the AI model interpreting free-form text, it fills in explicitly declared fields with specific types and descriptions.
Example: Fetching Weather Data (Structured)
In this format:
- The entire instruction is a single
fetchcode block containing YAML. - Field placeholders use a typed bracket syntax instead of plain
{location}. - Static values like
units: metricare passed through as-is. - Secrets are referenced with
${SECRET_DEFAULT}or${SECRET_NAME}and are resolved at execution time.
Field Placeholder Syntax
The structured format supports three types of field placeholders:
| Syntax | Name | Purpose |
|---|---|---|
$[name! ys|description] | AI-extracted field | The AI fills in this value based on the conversation |
((name! ys|description)) | Template placeholder | A value provided by the user or preset in the template |
${SECRET_DEFAULT} | Secret reference | Replaced with the resolved secret value at runtime |
Each placeholder has the following parts:
- name - the field identifier (e.g.,
location,query,apiKey) - ! - marks the field as required (omit for optional fields)
- ys - an operand that controls value formatting (ys = YAML string escaping)
- description - a human-readable hint that guides the AI or the user
Other common operands include:
| Operand | Description |
|---|---|
ys | YAML string - handles string escaping for YAML values |
euc | URL-encode the value |
js | JSON stringify the value |
trim | Trim whitespace from the value |
Structured YAML Properties
The structured fetch action supports the following top-level YAML properties:
| Property | Description |
|---|---|
method | HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS |
url | The base URL of the API endpoint |
path | URL path segments (can include dynamic fields) |
query | Query string parameters as key-value pairs |
headers | HTTP headers as key-value pairs |
body | Request body - a string or object for POST/PUT requests |
options.format | Response format: text, markdown, json, or toon |
The YAML Tags Format
The YAML tags format uses custom YAML tags like !fetch, !string, !number, !boolean, !reference, and !concat to define every part of the request in a fully typed, machine-parseable way. Instead of placeholder syntax like ((name! ys|description)), each dynamic field is an explicit YAML node with its own properties.
This format is defined in the platform's action tags system and is the most precise way to describe a fetch action.
Example: Fetching Weather Data (YAML Tags)
In this format:
- The
!fetchtag at the top declares this as a fetch action. !stringdeclares a typed input field with anameanddescription. The AI fills in the value based on the conversation.!reference SECRET_DEFAULTresolves to the secret linked to the ability, identical to${SECRET_DEFAULT}in other formats.- Static values like
units: metricare passed through unchanged. - You can add an
optionsblock (e.g.,format) exactly like in the structured format.
Available Field Tags
| Tag | Purpose | Key properties |
|---|---|---|
!string | A text input field | name, description, default, enum, min, max, transform |
!number | A numeric input field | name, description, default, enum, min, max |
!boolean | A true/false input field | name, description, default |
!array | An array input field | name, description, items (nested field schema) |
!object | An object input field | name, description, properties (named nested fields) |
Each tag also supports optional (defaults to false) and placeholder.
Appending ? to any field tag (e.g., !string?, !number?) is a shorthand for marking the field as optional.
Optional Fields
You can mark a field as optional either by setting the optional property or by using the shorthand ? tag variant:
Here, query is required and limit is optional with a default value of 10. If the user does not provide a limit, the default is used automatically.
Building Dynamic Strings with !concat
Use !concat to assemble a string from multiple parts, combining static text with dynamic field values:
!concat takes a sequence of values (strings, numbers, booleans, or field tags) and joins them into a single string.
Referencing Secrets with !reference
The !reference tag is a scalar tag that resolves named values at execution time. It is commonly used for secrets:
This is equivalent to ${SECRET_DEFAULT} in the other formats, but as a proper YAML node it integrates cleanly with the rest of the tag system.
Dynamic Query and Body Fields
You can mix static and dynamic values freely in query parameters and request bodies:
Field Transforms
String fields support transforms that modify the value before it is used:
Available transforms: lower, upper, trim, urlencode. Transforms are applied in order.
Field Enums
String and number fields can restrict their values to a predefined set using enum:
Simple vs Structured vs YAML Tags: Side-by-Side Comparison
Here is the same ability written in all three formats to highlight the differences.
Goal: Create an ability that searches for GitHub repositories by keyword.
Simple Format
Structured Format
YAML Tags Format
When to Use Each Format
| Consideration | Simple Format | Structured Format | YAML Tags Format |
|---|---|---|---|
| Ease of writing | Easiest - looks like a raw HTTP request | Moderate - YAML with placeholder syntax | Requires understanding YAML custom tags |
| AI flexibility | The model can improvise URL construction | The model fills in declared fields | The model fills in typed field nodes |
| Validation | No built-in input validation | Fields have required/optional markers | Full type system with defaults, enums, transforms |
| Response shaping | Returns the full response body | Can use options.format to control output | Can use options.format to control output |
| Authentication | Manual header construction | Secret references with ${SECRET_DEFAULT} | Secret references with !reference tag |
| Dynamic composition | Not supported | Limited to placeholder substitution | !concat for building strings from parts |
| Best for | Quick prototypes, simple public APIs | Production APIs, authenticated endpoints | Complex APIs, template libraries, programmatic use |
As a general rule, the simple format is great for getting started quickly, the structured format is better for production use, and the YAML tags format is ideal when you need full type safety, field validation, transforms, and dynamic string composition.
Authentication with Secrets
Most real-world APIs require authentication. ChatBotKit uses secrets to manage API keys, tokens, and OAuth credentials securely. Secrets are never exposed in the ability instruction or in conversation logs.
How Secrets Work
- You create a secret in ChatBotKit that stores your API credentials.
- You link the secret to your ability (or skillset).
- In the ability instruction, you reference the secret with
${SECRET_DEFAULT}or${SECRET_NAME}. - At execution time, ChatBotKit replaces the placeholder with the actual secret value, formatted according to its type.
Secret Types
| Type | How it authenticates | Example use case |
|---|---|---|
| Bearer | Adds Bearer <token> to the header value | Most REST APIs (OpenAI, Stripe, etc.) |
| Plain | Uses the raw value without transformation | APIs that expect a key in a query parameter |
| Basic | Base64-encodes username:password with Basic prefix | Legacy HTTP Basic Auth APIs |
| OAuth | Manages a full OAuth 2.0 token flow with automatic refresh | Google, Slack, GitHub OAuth apps |
Step-by-Step: Adding a Bearer Token Secret
- Navigate to Secrets in your ChatBotKit dashboard.
- Click Create Secret.
- Choose the Bearer type.
- Paste your API token into the value field.
- Save the secret - note the secret ID.
Then, in your ability:
When the ability runs, ${SECRET_DEFAULT} is replaced with Bearer your-api-token-here. You do not need to type the Bearer prefix yourself - ChatBotKit adds it automatically for bearer-type secrets.
Step-by-Step: Using a Plain API Key
Some APIs expect the key as a query parameter rather than a header. In that case, use a plain secret:
For a plain secret, ChatBotKit inserts the raw key value without any prefix.
Named Secrets
If your ability needs multiple credentials (for example, an API key and a separate webhook secret), you can use named secrets instead of the default:
Each ${SECRET_NAME} placeholder is resolved independently. The SECRET_DEFAULT refers to the primary secret linked to the ability, while any other name (like SECRET_WEBHOOK) refers to a secret with that name in your account.
Admin-Managed vs User-Authenticated Secrets
ChatBotKit supports two models for secret ownership, depending on who provides and manages the credentials.
Admin-Managed Secrets (Shared)
With shared secrets, the administrator provides the API key or token once, and every user of the bot benefits from it. This is the most common model for company-internal bots.
How it works:
- The admin creates a secret in the ChatBotKit dashboard.
- The admin links the secret to the ability or skillset.
- All conversations automatically use the admin's credentials.
Use cases:
- Internal tools where the company owns the API subscription
- Bots that query public APIs with a single API key
- Team-shared bots with centralized billing
User-Authenticated Secrets (Personal)
With personal secrets, each end user authenticates individually. This is typically used for OAuth integrations where the bot needs to access user-specific data.
How it works:
- The admin configures an OAuth secret with the client ID, client secret, authorization URL, and token URL.
- When a user interacts with the bot and triggers the ability, they are prompted to authorize access through the OAuth flow.
- Each user's token is stored separately and used only for their conversations.
Use cases:
- Accessing a user's Google Calendar or Gmail
- Reading a user's Slack messages
- Managing a user's GitHub repositories
Example: Google Calendar OAuth Ability
First, create an OAuth secret for Google Calendar with the following configuration:
| Field | Value |
|---|---|
| Type | OAuth |
| Client ID | Your Google OAuth client ID |
| Client Secret | Your Google OAuth client secret |
| Authorization URL | https://accounts.google.com/o/oauth2/auth?access_type=offline&prompt=consent |
| Token URL | https://accounts.google.com/o/oauth2/token |
| Scope | https://www.googleapis.com/auth/calendar.readonly |
Then, create an ability that uses this secret:
When a user triggers this ability for the first time, they will be guided through the Google OAuth consent screen. After authorization, the bot can fetch their calendar events.
Putting It All Together
Let's build a complete example - a bot that fetches cryptocurrency prices from an API that requires an API key.
Step 1: Create the Secret
- Go to Secrets in the ChatBotKit dashboard.
- Create a new Plain secret.
- Paste your CoinAPI key.
- Name it appropriately (e.g., "CoinAPI Key").
Step 2: Create the Skillset and Ability
- Create a new Skillset named "Cryptocurrency".
- Add an ability named get_crypto_price with the description: "Get the current price of a cryptocurrency."
- Link the secret you created in Step 1 to this ability.
- Set the instruction to:
Or equivalently, using the YAML tags format:
Step 3: Connect and Test
- Attach the Cryptocurrency skillset to your bot.
- Open the bot in the playground.
- Ask: "What is the current price of Bitcoin?"
The bot will call the CoinAPI endpoint, extract the exchange rate, and respond with the formatted result.
Troubleshooting
Ability does not execute the fetch request
- Verify the instruction is a valid YAML document inside the
fetchcode block. - Check that required fields are marked with
!(e.g.,((query! ys|...))). - Ensure the bot's model supports function/tool calling.
Authentication errors (401 or 403)
- Confirm the secret is linked to the ability.
- Check the secret type matches the API's expected format (Bearer, Plain, etc.).
- For OAuth secrets, verify the scopes include the permissions the API requires.
Empty or unexpected responses
- Test the API URL directly in a browser or with curl to confirm it returns data.
- Try removing the
optionsblock temporarily to see the full raw response.
Next Steps
- Explore the Ability Catalogue for pre-built fetch abilities you can install with one click
- Learn about MCP server integrations for more advanced tool-use patterns
- Read the Weather Forecast Bot tutorial for a beginner-friendly introduction to skillsets