Browse all guides
Connect the bot to any backend system
Custom tools let the AI agent call your backend during a live conversation, so answers come from real data instead of static content only: order status from an ERP, delivery tracking, support tickets, appointment availability, account data or CRM updates.
Credentials
The secret and configuration values needed to reach your backend, stored separately from the code so nothing is hardcoded.
- API key, bearer token or username and password
- Base API URL, tenant ID, company ID
- Read in function code as credentials[:ERP_BASE_URL]
- Use credentials for anything that differs per customer or environment
Functions
One function is one backend action the bot can perform: a small API operation it calls when needed.
- Name: technical name in snake_case, e.g. get_order_delivery_status
- Description: tells the AI when the function should be used
- Parameters: the data needed from the customer or conversation
- Code: the logic that calls your API, returning structured data
Roles and intents
A role tells the bot when a function is relevant in a conversation, and how to answer after calling it.
- Description: what the customer is asking about
- Instructions: which details to collect and how to answer
- Examples: real customer phrasings that trigger the role
- Without a role, the bot may never call your function
How the pieces work together
- 1Customer asks
"Where is my order 100391?"
- 2Role match
The bot recognizes the Order delivery status intent.
- 3Missing data
If the email is missing, the bot asks for the address used for the order.
- 4Function call
The bot calls get_order_delivery_status with order_id and email.
- 5Live backend data
Your ERP is called with the stored credentials and returns live order and delivery data.
- 6Natural answer
"Your order 100391 has shipped and is in transit with DHL. Tracking ID JD014600009876543210, estimated delivery August 8."
Setup example: ERP order status tool
An online shop connects the bot to an ERP that holds customer orders and delivery data.
1. Create the custom tool
Give it a name such as ERP Order Lookup and a description like "Looks up customer order and delivery status from the ERP system." The tool groups all related functions.
2. Add credentials
Store the connection values as keys. They stay server side and are never exposed in the conversation.
ERP_BASE_URL = https://erp.example-shop.com
ERP_API_KEY = your-api-key3. Add the function
Define the name, a description that explains when to use it, and the parameter schema the AI must fill.
{
"type": "object",
"properties": {
"order_id": { "type": "string", "description": "The customer's order number" },
"email": { "type": "string", "description": "The email used for the order" }
},
"required": ["order_id", "email"]
}4. Write the function code
Read credentials and arguments, call your API, then return a structured success response with formatting guidelines for the answer.
base_url = credentials[:ERP_BASE_URL]
api_key = credentials[:ERP_API_KEY]
order_id = arguments[:order_id].to_s.strip
email = arguments[:email].to_s.strip
if base_url.blank? || api_key.blank?
failure_response("ERP credentials are missing")
elsif order_id.blank? || email.blank?
failure_response("Order number and email are required")
else
endpoint = "#{base_url}/api/orders/status"
headers = { "Authorization" => "Bearer #{api_key}", "Content-Type" => "application/json" }
params = { "order_id" => order_id, "email" => email }
response = make_api_call(endpoint, params, :get, headers, false)
if api_call_succeeded?(response)
order = response[:result]
success_response({
"order_id" => order_id,
"status" => order["status"],
"delivery_status" => order["delivery_status"],
"carrier" => order["carrier"],
"tracking_id" => order["tracking_id"],
"tracking_url" => order["tracking_url"],
"estimated_delivery_date" => order["estimated_delivery_date"],
"formatting_guidelines" => "Answer briefly. Mention order status, delivery status, carrier, tracking ID and estimated delivery date if available. Do not expose internal ERP fields."
})
else
response
end
end5. Add the role or intent
Connect customer language to the function, then select get_order_delivery_status for this role.
Name: Order delivery status
Description: Customer asks where their order is, whether it shipped,
when it arrives, or asks for tracking information.
Instructions: Ask for the order number and email if missing. Use the ERP
order lookup function. Give the delivery status concisely and include the
tracking ID and link when available. If the order is not found, ask the
customer to check the order number and email.
Examples:
Where is my order 100391?
Has my package shipped yet?
Can I get the tracking number for my order?
When will my order arrive?Setup example: Find the nearest office or branch
Use OpenStreetMap to locate a visitor from their postcode, address, landmark or district, then return the three closest company locations with driving distance and time.
1. Create the Custom API tool
Name: OpenStreetMap office finder Description: Finds the company office nearest to a visitor’s stated location using OpenStreetMap geocoding and routing.
2. Add a credential
Add the following credential:
api_key = YourCompany Office Finder (support@yourcompany.com)This value is used as the User-Agent for OpenStreetMap requests. Use a clear application name and contact address.
3. Add office locations to the knowledge base
Create or upload a source containing each office’s: - Name - Full address - Latitude - Longitude - Optional information, such as opening hours or a service note Example:
[
{
"name": "Helsinki Office",
"address": "Example Street 1, 00100 Helsinki",
"lat": 60.1699,
"lon": 24.9384,
"info": "Open weekdays 09:00–17:00"
}
]4. Create the function
Function name: find_nearest_office Description: Find up to three of the company’s offices nearest to a visitor’s stated location. Call only after the visitor has provided a postcode and city, street address, landmark, or district and city. Use this parameter schema:
{
"type": "object",
"properties": {
"location_text": {
"type": "string",
"description": "The visitor's stated location. Never guess it."
},
"locations": {
"type": "array",
"description": "The company's office locations. Retrieve these from the knowledge base; never ask the visitor for them.",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"address": { "type": "string" },
"lat": { "type": "number" },
"lon": { "type": "number" },
"info": { "type": "string" }
},
"required": ["name", "address", "lat", "lon"]
}
}
},
"required": ["location_text"]
}Configure locations to be populated from the knowledge base. The function should receive office locations automatically, rather than asking the visitor to choose from a list.
5. Add the function code
Use the configured implementation, changing country_hint if your business operates primarily outside Finland:
location_text = arguments[:location_text].to_s.strip
user_agent = credentials[:api_key].to_s.strip
country_hint = "Finland"
offices = (arguments[:locations] || []).take(20)
usable_offices = offices.select do |office|
office["name"].present? &&
office["lat"].present? &&
office["lon"].present? &&
office["address"].present?
end
if location_text.blank?
success_response({
"status" => "location_needed",
"message" => "Ask the visitor for a postcode and city, street address, landmark, or district and city."
})
elsif user_agent.blank?
failure_response("Office finder User-Agent is missing")
elsif usable_offices.empty?
success_response({
"status" => "office_locations_unavailable",
"message" => "No office locations with coordinates were available from the knowledge base."
})
else
query = "#{location_text}, #{country_hint}"
geocode_response = make_api_call(
"https://nominatim.openstreetmap.org/search",
{ "q" => query, "format" => "jsonv2", "limit" => 1, "addressdetails" => 0 },
:get,
{ "User-Agent" => user_agent, "Accept-Language" => "en" },
false
)
unless api_call_succeeded?(geocode_response)
geocode_response
else
matches = geocode_response[:result] || []
if matches.empty?
success_response({
"status" => "location_not_found",
"message" => "Ask the visitor for a more precise street address or nearby landmark and city."
})
else
origin = matches.first
origin_lat = origin["lat"].to_f
origin_lon = origin["lon"].to_f
coordinates = usable_offices.map { |office| "#{office["lon"]},#{office["lat"]}" }.join(";")
route_response = make_api_call(
"https://router.project-osrm.org/table/v1/driving/#{origin_lon},#{origin_lat};#{coordinates}",
{ "annotations" => "distance,duration" },
:get,
{},
false
)
unless api_call_succeeded?(route_response)
route_response
else
data = route_response[:result] || {}
distances = (data["distances"] || []).first || []
durations = (data["durations"] || []).first || []
results = usable_offices.each_with_index.filter_map do |office, index|
distance = distances[index + 1]
duration = durations[index + 1]
next unless distance.present? && duration.present?
{
"name" => office["name"],
"address" => office["address"],
"info" => office["info"],
"driving_distance_km" => format("%.1f", distance.to_f / 1000),
"driving_duration_minutes" => (duration.to_f / 60).to_i,
"maps_url" => "https://www.openstreetmap.org/directions?engine=fossgis_osrm_car&route=#{origin_lat}%2C#{origin_lon}%3B#{office["lat"]}%2C#{office["lon"]}"
}
end.sort_by { |office| office["driving_distance_km"].to_f }.take(3)
success_response({
"status" => "ok",
"offices" => results,
"attribution" => "© OpenStreetMap contributors",
"attribution_url" => "https://www.openstreetmap.org/copyright"
})
end
end
end
end6. Create the role or intent
Name: Find nearest office Description: The visitor asks which company office, branch, store, or service point is nearest to them. Instructions: Ask for the visitor’s postcode and city, street address, landmark, or district and city if they have not provided one. Never infer their location. Call find_nearest_office as soon as a usable location is available. Present offices nearest first, including name, address, driving distance, estimated driving time, available opening-hours information, and the map link. Do not repeat the visitor’s home address. Include this attribution: © OpenStreetMap contributors If the location cannot be found, ask for a more precise address or nearby landmark. If office data is unavailable or the tool fails, explain that the nearest office cannot currently be determined: never guess.
Security notes
- •The backend must verify that the order belongs to the provided email address. Never return order details based on an order number alone.
- •Do not expose internal fields, raw API errors, credentials, customer IDs or unnecessary personal data to the customer.
- •Write clear function descriptions and role instructions: most tool failures come from the bot not knowing when to call the function.
Ask more from LastBot support
Send us your question and our team will get back to you.