Turn the spreadsheet you already run your business on into a real-time data source for your echowin AI agent — no code platform required.
Most small businesses run on Google Sheets. Pricing lists, service menus, appointment availability, inventory counts, staff schedules — if it's important, there's a good chance it lives in a spreadsheet somewhere. The problem is that when a customer calls and your AI agent picks up, it doesn't know what's in that spreadsheet. It only knows what's in its knowledgebase.
Until now.
Using echowin's Call API (Webhooks) tool and a simple Google Apps Script, you can give your AI agent live, read-only access to any Google Sheet. When a caller asks about pricing, availability, or anything else that changes regularly, your agent fetches the latest data straight from the spreadsheet — the same one your team is already updating — and uses it in the conversation.
No syncing. No manual updates. No stale information. Just one source of truth, accessible to both your team and your AI.
If you've been using echowin for a while, you've probably run into this friction: you update a price, change your holiday hours, or add a new service — and then you have to remember to also update your AI agent's knowledgebase. Sometimes you forget, and a caller gets outdated information.
Connecting your agent directly to Google Sheets eliminates that problem entirely. Update the spreadsheet, and your agent automatically knows.
Here are a few real examples of how echowin users are putting this to work:
A plumbing company keeps a pricing sheet with their service rates. When a caller asks "How much does a drain cleaning cost?", the agent looks it up live instead of relying on a static price that might have changed last month.
A property management firm tracks unit availability in a shared spreadsheet. Their AI agent can tell callers which apartments are currently vacant, what the rent is, and when they're available for move-in — all pulled from the sheet in real time.
A med spa maintains a Google Sheet with their weekly specials and promotional pricing. Their agent mentions the current promo to every caller without anyone having to update the agent's instructions each week.
A catering company lists their menu packages and seasonal items in a spreadsheet. When a caller asks what's available for a summer event, the agent pulls the current seasonal menu — not last quarter's.
The pattern is always the same: if your team updates a spreadsheet, your AI agent can read it.
Here's the flow in plain English:
The caller just hears a fast, accurate answer. Behind the scenes, your agent just read a spreadsheet in real time.
For a full reference on the webhook tool itself, see the Call API (Webhooks) documentation.
For this walkthrough, let's say you run a home services company and you want your agent to quote prices from a live pricing sheet.
Create a Google Sheet (or open your existing one) with a structure like this:
| Service | Price | Notes |
|---|---|---|
| Drain Cleaning | $150 | Standard residential |
| Water Heater Install | $1,200 | 50-gal tank, parts included |
| Leak Detection | $95 | Diagnostic fee, credited toward repair |
| Faucet Replacement | $250 | Labor + standard fixture |
| Emergency Call-Out | $200 | After-hours surcharge |
Keep the first row as headers. The script we'll write in the next step reads this structure automatically.
Tip: Your sheet can have as many columns and rows as you need. The script will return everything, and you can use echowin's response identifiers to pick out just the fields your agent should use.
Now we'll turn this spreadsheet into an API endpoint.
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
// First row is headers
var headers = data[0];
var results = [];
// Check if a search query was provided
var searchQuery = e.parameter.q ? e.parameter.q.toLowerCase() : null;
for (var i = 1; i < data.length; i++) {
var row = {};
for (var j = 0; j < headers.length; j++) {
row[headers[j].toLowerCase().replace(/\s+/g, '_')] = data[i][j];
}
// If there's a search query, filter rows
if (searchQuery) {
var rowText = data[i].join(' ').toLowerCase();
if (rowText.indexOf(searchQuery) !== -1) {
results.push(row);
}
} else {
results.push(row);
}
}
var output = {
total: results.length,
data: results
};
return ContentService
.createTextOutput(JSON.stringify(output))
.setMimeType(ContentService.MimeType.JSON);
}
Here's what this script does:
service and "Water Heater Install" stays readable).?q= search parameter is included in the request, it filters rows that match the query. If no query is provided, it returns everything.https://script.google.com/macros/s/AKfycbx...long-string.../exec
This URL is now a live API endpoint. Anyone (or any system, like echowin) that hits this URL gets back the current contents of your spreadsheet as JSON.
Test it by pasting the URL into your browser. You should see something like:
{
"total": 5,
"data": [
{
"service": "Drain Cleaning",
"price": "$150",
"notes": "Standard residential"
},
{
"service": "Water Heater Install",
"price": "$1,200",
"notes": "50-gal tank, parts included"
}
]
}
Try adding a search query: YOUR_URL?q=drain. You should see only the matching row(s).
Head to your echowin agent's settings and add a new Call API (Webhooks) tool. The configuration panel has two sides: the Configuration form on the left and the Test Playground on the right. Here's how to fill it out:
Description: Give the tool a short name your agent can reference. This is how you'll refer to it in your instructions.
Look Up Service Pricing
URL: Paste the web app URL you copied when deploying your Apps Script.
https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec
Method: Select GET from the dropdown.
Headers: Toggle this on. The default Content-Type: application/json is fine.
Query Parameters: Toggle this on, then click + Add Parameter and add one key-value pair:
| Key | Value |
|---|---|
q |
$search_term |
The $search_term part is an echowin variable. When a caller says "How much is a drain cleaning?", your agent extracts "drain cleaning" and passes it as the value of q. You'll see $search_term appear under Detected Variables at the bottom of the configuration panel — this confirms echowin has picked it up.
Response Mapping: Toggle this on, then click + Add Response Key for each field you want the agent to access:
| Response Key |
|---|
data |
total |
These tell the agent which parts of the JSON response it can use. If you want the agent to access nested fields, use dot notation (e.g., data.0.service for the first result's service name).
Body: Leave this empty — GET requests don't need one.
Before going live, use the Test Playground panel on the right side of the configuration screen:
$search_term. Enter a test value like "drain".If the test returns your spreadsheet data, you're good to go. If not, double-check that your Apps Script is deployed and the URL is correct.
Tell your agent when and how to use this new tool. Add something like this to your instructions:
You have access to a "Look Up Service Pricing" tool that retrieves live pricing
from our service catalog.
When a caller asks about:
- How much a service costs
- What services we offer
- Pricing for a specific job
Use the tool with $search_term set to the relevant service name or keyword.
For example, if a caller asks "How much does it cost to fix a leak?", set
$search_term to "leak".
When you get results back:
- Quote the price and include any relevant notes.
- If multiple results match, briefly list the options and ask the caller
which one they're interested in.
- If no results are found, say: "I don't see that specific service in our
current pricing. Let me have someone from our team call you back with
a custom quote."
Always present prices confidently. These are our current, up-to-date rates.
Call your echowin number and test with questions like:
Your agent will call the webhook, search your Google Sheet, and respond with the live data. Check your echowin call logs to see the full exchange including the webhook request and response.
The pricing lookup is just one example. Here are other ways echowin users are connecting their agents to Google Sheets:
Keep a sheet with who's working each day. When a caller asks to speak to a specific person or needs to reach the on-call technician, the agent checks the schedule and either transfers the call or takes a message.
| Day | On Call | Phone |
|---|---|---|
| Monday | Sarah | 555-0101 |
| Tuesday | Mike | 555-0102 |
| Wednesday | Sarah | 555-0101 |
Your agent's instruction: "When a caller asks for the on-call technician, use the schedule tool to find today's on-call person, then transfer the call to their number."
A retail business tracks stock in a spreadsheet. The agent can tell callers whether an item is in stock without putting them on hold.
A yoga studio, gym, or community center keeps class times in a sheet. The agent reads the schedule and tells callers what's available today or this week.
Instead of updating your knowledgebase every time a policy changes, maintain an FAQ sheet. Your agent queries it live so the answer is always current.
With a small modification to the Apps Script (using doPost instead of doGet), your agent can also write data to the sheet — for example, logging a caller's name, number, and reason for calling as a new row in your leads spreadsheet. We'll cover that in the advanced section below.
Now your agent can both read from and write to the same spreadsheet your team uses every day.
The pricing example above uses a simple search — but what about when a caller needs to look up their own record? Things like order status, appointment confirmations, or account details?
This is one of the most common Google Sheets integrations echowin users build, and it follows a slightly different pattern. Instead of a broad keyword search, the agent collects specific identifying information from the caller (like their last name and zip code), passes it as query parameters, and gets back a personalized response.
Here's a full production-ready example. We've used this pattern for echowin clients in industries like energy, insurance, and property management — any business where customers call to check on the status of something.
Say you run a service where customers submit applications or orders, and your team tracks them in a Google Sheet like this:
| Last Name | Zip Code | DOB | Submit Date | Status | Account # | Notes |
|---|---|---|---|---|---|---|
| Johnson | 77001 | 03/15/1990 | 03/14/2026 | complete | ACC-4821 | Approved, no issues |
| Martinez | 90210 | 07/22/1985 | 03/13/2026 | pending_review | Docs under review | |
| Williams | 30301 | 11/08/1978 | 03/15/2026 | declined | Failed credit check |
This script handles incoming GET requests from your echowin agent, matches the caller's info against the sheet, and returns a tailored response based on their account status:
// ============================================================
// Customer Account Status Lookup API
// ============================================================
var SHEET_NAME = "Sheet1";
// ---- Handle incoming GET requests from echowin ----
function doGet(e) {
try {
var lastName = (e.parameter.last_name || "").trim().toLowerCase();
var zipCode = (e.parameter.zip_code || "").trim();
var dob = (e.parameter.dob || "").trim();
// Validate that all required fields were provided
if (!lastName || !zipCode || !dob) {
return sendResponse({
status: "error",
account_number: null,
message: "Missing required fields. Please provide last name, zip code, and date of birth."
});
}
// Search the sheet for a matching record
var result = lookupAccount(lastName, zipCode, dob);
// Log every query for auditing (optional)
logQuery(lastName, zipCode, dob, result.status);
return sendResponse(result);
} catch (err) {
return sendResponse({
status: "error",
account_number: null,
message: "Something went wrong. Please try again or contact support."
});
}
}
// ---- Also support POST requests if needed ----
function doPost(e) {
var body = JSON.parse(e.postData.contents);
var lastName = (body.last_name || "").trim().toLowerCase();
var zipCode = (body.zip_code || "").trim();
var dob = (body.dob || "").trim();
if (!lastName || !zipCode || !dob) {
return sendResponse({
status: "error",
account_number: null,
message: "Missing required fields: last_name, zip_code, and dob."
});
}
var result = lookupAccount(lastName, zipCode, dob);
logQuery(lastName, zipCode, dob, result.status);
return sendResponse(result);
}
// ---- Core lookup logic ----
function lookupAccount(lastName, zipCode, dob) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
var data = sheet.getDataRange().getValues();
// Skip header row, search through data
for (var i = 1; i < data.length; i++) {
var row = data[i];
var rowLastName = String(row[0]).trim().toLowerCase();
var rowZip = String(row[1]).trim();
var rowDob = formatDate(row[2]);
var rowStatus = String(row[4]).trim().toLowerCase();
var rowAccountNum = String(row[5]).trim();
// Match on all three identifying fields
if (rowLastName === lastName && rowZip === zipCode && rowDob === dob) {
// Return a tailored response based on status
switch (rowStatus) {
case "complete":
return {
status: "complete",
account_number: rowAccountNum || "N/A",
message: "Your application has been approved. Your account number is "
+ (rowAccountNum || "N/A")
+ ". Please check your email for next steps."
};
case "pending_review":
return {
status: "pending_review",
account_number: null,
message: "Your application is currently under review. Our team is "
+ "processing it and you should hear back within 1-2 business days. "
+ "Please check your email for any updates."
};
case "declined":
return {
status: "declined",
account_number: null,
message: "Unfortunately your application was not approved. Please check "
+ "your email for details, or contact our support team for more information."
};
default:
return {
status: "unknown",
account_number: null,
message: "We found your record but need to look into the details. "
+ "Please contact our support team for assistance."
};
}
}
}
// No match found
return {
status: "not_found",
account_number: null,
message: "We were unable to find a matching record. Please double-check "
+ "your information, or contact our support team for help."
};
}
// ---- Format date values to MM/DD/YYYY ----
function formatDate(value) {
if (value instanceof Date) {
var m = ("0" + (value.getMonth() + 1)).slice(-2);
var d = ("0" + value.getDate()).slice(-2);
var y = value.getFullYear();
return m + "/" + d + "/" + y;
}
return String(value).trim();
}
// ---- Send JSON response ----
function sendResponse(data) {
return ContentService
.createTextOutput(JSON.stringify(data))
.setMimeType(ContentService.MimeType.JSON);
}
// ---- Log every query to a separate tab for auditing ----
function logQuery(lastName, zipCode, dob, resultStatus) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var logSheet = ss.getSheetByName("Query Log");
if (!logSheet) {
logSheet = ss.insertSheet("Query Log");
logSheet.appendRow(["Timestamp", "Last Name", "Zip Code", "DOB", "Result Status"]);
}
logSheet.appendRow([new Date(), lastName, zipCode, dob, resultStatus]);
}
A few things worth noting about this script:
Input validation. The script checks that all required fields are present before searching. If the agent sends an incomplete request (maybe the caller only gave their name but not their zip), the API returns a clear error message that the agent can relay.
Status-based responses. Instead of returning raw data and hoping the agent figures out what to say, the script returns a pre-written message for each status. This gives you tight control over exactly what the caller hears for each scenario — approved, pending, declined, or not found.
Query logging. Every lookup is automatically logged to a separate "Query Log" tab with a timestamp. This gives you an audit trail of every account lookup your agent performs, which is invaluable for compliance and debugging.
Easy to test. Since the primary handler is doGet, you can test the lookup right from your browser by pasting the URL with query parameters — no Postman or cURL needed. The doPost handler is also included as an alternative if you prefer sending data in a request body.
In echowin, open your agent's tools and add a new Call API (Webhooks) tool. Here's how to fill out each field — this matches exactly what you'll see in the configuration panel:
Description:
Check Account Status
URL:
https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec
Method:
GET
Headers: Toggle on, and leave the defaults (Content-Type: application/json).
Query Parameters: Toggle on, and add three key-value pairs:
| Key | Value |
|---|---|
last_name |
$lastname |
zip_code |
$zip |
dob |
$dob |
These are echowin variables — during a live call, the agent automatically fills them in with the information it collects from the caller. You'll see them appear under Detected Variables at the bottom of the config panel.
Response Mapping: Toggle on, and add three keys:
| Response Key |
|---|
status |
account_number |
message |
These tell the agent which fields from the JSON response it can use in conversation. You can also add these by clicking on keys in the Test Playground response after a successful test.
Body: Leave empty — this is a GET request.
Tip: Use the Test Playground on the right side of the config panel to verify everything works. Enter sample values for
$lastname,$zip, and$dob, click Send Request, and confirm the response comes back with the expectedstatus,account_number, andmessagefields.
Here's where it all comes together. The instructions below tell your agent exactly how to handle the lookup — collecting information one step at a time and responding differently based on each status. This is the pattern that works in production:
When a caller asks about the status of their application, order, or account,
collect the following three pieces of information ONE AT A TIME:
1. Last Name
"Sure, I can look that up for you! What is the last name on the account?"
2. Zip Code
"Thanks! And what is the 5-digit zip code on your account?"
3. Date of Birth
"Got it. Last one — what is your date of birth?"
IMPORTANT: Make sure you have ALL 3 pieces of information before proceeding.
Do NOT trigger the tool until you have collected all three. Date of birth
should be in MM/DD/YYYY format.
Once all three fields are collected, trigger the "Check Account Status"
webhook with: $lastname, $zip, and $dob.
Then respond based on the status returned:
Status: "complete"
"Great news — your application has been approved! Your account number is
[account_number]. Please check your email inbox for additional details
and next steps."
Status: "pending_review"
"It looks like your application is currently under review. Our team is
working on it and you should hear back soon. Please keep an eye on your
email inbox for updates."
Status: "declined"
"It looks like your application was not approved. Please check your email
inbox for more details, or contact our support team during business hours
for further information."
Status: "not_found"
"I wasn't able to find a matching record. Could you double-check the
information you provided? If everything looks correct, please contact
our support team for assistance."
Status: "error" or webhook failure
"I'm having trouble looking that up right now. Please reach out to our
support team for help with your account status."
After providing any status response, always ask:
"Is there anything else I can help you with?"
Notice a few things about these instructions:
One at a time. The agent collects each field separately with a natural prompt. This prevents the caller from being overwhelmed by a list of three things to rattle off at once, and it gives the agent a clear value for each variable.
Gate before triggering. The explicit "do NOT trigger the tool until you have all three" instruction prevents the agent from firing the webhook with missing data — which would just return an error.
Script the responses per status. By writing out exactly what the agent should say for each status, you maintain full control over the messaging. The agent doesn't have to improvise around sensitive topics like declined applications.
Always offer to continue. Ending with "Is there anything else I can help you with?" keeps the conversation open and professional.
Here's how a typical call plays out:
Caller: "Hi, I submitted an application a couple days ago and I'm wondering what the status is."
Agent: "Sure, I can look that up for you! What is the last name on the account?"
Caller: "Johnson."
Agent: "Thanks! And what is the 5-digit zip code on your account?"
Caller: "77001."
Agent: "Got it. Last one — what is your date of birth?"
Caller: "March 15th, 1990."
(Agent triggers the webhook, gets a response in seconds)
Agent: "Great news — your application has been approved! Your account number is ACC-4821. Please check your email inbox for additional details and next steps. Is there anything else I can help you with?"
The caller gets a fast, accurate, personalized answer. Your team doesn't have to take the call. And the data came straight from the same Google Sheet your operations team is already working in.
Keep your sheet clean. The script reads the first row as headers and every row below as data. Avoid blank rows in the middle of your data, merged cells, or extra header rows — they'll confuse the output.
Use descriptive column headers. The script converts them to JSON keys. "Service" becomes service, "Monthly Price" becomes monthly_price. Clear headers make your agent's response identifiers easier to work with.
Redeploy after script changes. If you edit the Apps Script code, you need to create a new deployment (Deploy → New Deployment) for the changes to take effect. The URL will change, so update it in echowin's webhook config too. Alternatively, you can update an existing deployment under Deploy → Manage Deployments.
Be mindful of rate limits. Google Apps Script web apps can handle a solid amount of traffic, but they're not built for thousands of requests per minute. For a typical small business call volume, you'll never hit the ceiling. If you're running a high-volume call center, consider caching or a dedicated API.
Restrict data if needed. The script above returns all columns. If your sheet contains sensitive data you don't want the agent to access (like cost margins or internal notes), either move that data to a separate sheet or modify the script to exclude specific columns.
Test from the echowin playground first, every time. Make a change to your sheet, test the webhook in the playground, and confirm the data comes through correctly before any caller hears it.
Your business probably already has a Google Sheet that serves as the source of truth for your pricing, availability, staff schedules, or inventory. With a five-minute Apps Script setup and echowin's webhook tool, that spreadsheet becomes a live data feed for your AI agent.
No more manually syncing your knowledgebase every time a price changes. No more callers getting outdated information. Just one spreadsheet that your team updates and your AI agent reads — in real time, on every call.
Ready to connect your first sheet? Head to your echowin dashboard, open your agent's tools, and add a new Call API webhook. The webhook documentation has the full reference.
Have questions about connecting Google Sheets to your agent? Contact the echowin team — we're happy to walk you through it.
Artificial intelligence (AI) is no longer a futuristic concept reserved for large corporations with deep pockets. Today, AI is transforming the way small to medium businesses (SMBs) operate, making them more efficient, cost-effective, and competitive.
While we have several other tools and processes in place to ensure product reliability and quality, this visibility & transparency driven approach has helped us stay close to our customers. By growing alongside them, we build confidence, trust, and a stronger AI-driven future together.
Mastering active listening is vital for entrepreneurs to enhance phone communication. Techniques like staying focused, using verbal cues, paraphrasing, and leveraging AI tools like echowin can improve customer satisfaction and ensure clients feel valued. Discover more tips and explore echowin's services for streamlined business communications.
Streamline Your Business Operations with Technology & Innovation - get expert tips, proven strategies, and actionable insights delivered straight to your inbox!
Subscribe to our blog

Join thousands of businesses automating their operations with echowin