<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Vicky Lalwani]]></title><description><![CDATA[Vicky Lalwani]]></description><link>https://vickylalwani.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 03 Nov 2026 12:38:36 GMT</lastBuildDate><atom:link href="https://vickylalwani.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Google Ads conversion tracking in 2027: a 10 point checklist before you scale spend]]></title><description><![CDATA[Smart Bidding can only optimise toward what you measure. If your conversion tracking counts the wrong actions, counts them twice, or misses half of them…]]></description><link>https://vickylalwani.hashnode.dev/google-ads-conversion-tracking-in-2027-a-10-point-checklist-before-you-scale-spend</link><guid isPermaLink="true">https://vickylalwani.hashnode.dev/google-ads-conversion-tracking-in-2027-a-10-point-checklist-before-you-scale-spend</guid><category><![CDATA[google ads]]></category><category><![CDATA[ppc]]></category><category><![CDATA[marketing]]></category><category><![CDATA[analytics]]></category><dc:creator><![CDATA[Vicky Lalwani]]></dc:creator><pubDate>Sat, 31 Oct 2026 23:35:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae508cebc36b9add06f3c7e/58429c99-7b63-49b3-b197-49f06b4b64ed.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>TL;DR</h2>
<p>Smart Bidding can only optimise toward what you measure. If your conversion tracking counts the wrong actions, counts them twice, or misses half of them, raising the budget just buys more of the wrong thing. Run the ten checks below before you scale any Google Ads account.</p>
<h2>Why tracking comes before budget</h2>
<table>
<thead>
<tr>
<th>Symptom you see</th>
<th>Likely tracking cause</th>
<th>What it costs you</th>
</tr>
</thead>
<tbody><tr>
<td>Cost per lead looks great, sales team says leads are poor</td>
<td>Page views or button clicks counted as primary conversions</td>
<td>Bidding chases cheap, low intent clicks</td>
</tr>
<tr>
<td>Conversions jump overnight with no change in sales</td>
<td>Duplicate tags or a tag firing on every page load</td>
<td>Inflated results and overspend</td>
</tr>
<tr>
<td>Campaigns stuck in "learning"</td>
<td>Too few conversions, or conversions arriving late</td>
<td>Unstable bids and wasted budget</td>
</tr>
<tr>
<td>Reported conversions far below CRM leads</td>
<td>Consent, cross domain or form tracking gaps</td>
<td>Bidding undervalues good campaigns</td>
</tr>
</tbody></table>
<h2>The 10 point checklist</h2>
<h3>1. List every conversion action and its job</h3>
<p>Open the conversions list and write down what each action measures. Mark each one as a real business outcome (lead, purchase, booked call) or a micro signal (scroll, video view, button click).</p>
<h3>2. Keep only real outcomes as primary</h3>
<p>Only primary actions feed bidding. Micro signals can stay as secondary for reporting. This one change fixes more accounts than any bid strategy tweak.</p>
<h3>3. Check for duplicates</h3>
<p>Look for the same action tracked twice, for example a Google Ads tag and an imported analytics goal both counting one form fill. Pick one source per action.</p>
<h3>4. Set the right counting rule</h3>
<table>
<thead>
<tr>
<th>Business type</th>
<th>Counting setting</th>
<th>Why</th>
</tr>
</thead>
<tbody><tr>
<td>Lead generation</td>
<td>One per click</td>
<td>One person submitting twice is still one lead</td>
</tr>
<tr>
<td>Ecommerce</td>
<td>Every</td>
<td>Each purchase is real revenue</td>
</tr>
</tbody></table>
<h3>5. Use enhanced conversions</h3>
<p>Enhanced conversions send hashed first party data from your form or checkout so Google can match more conversions. It is free to switch on and usually recovers conversions lost to cookie limits.</p>
<h3>6. Confirm consent mode is working</h3>
<p>If you serve visitors in the UK, EU or other regions with consent rules, check that consent mode is set up and that tags respect the visitor's choice. A broken consent setup can silently drop a large share of conversions.</p>
<h3>7. Test the thank you path end to end</h3>
<p>Submit a real test lead or order. Confirm the tag fires once, on the right page, and that the conversion appears in the account within the expected delay. Use Tag Assistant or your tag manager's preview mode, both free.</p>
<h3>8. Import offline outcomes</h3>
<p>If sales happen after the form fill, import qualified leads or closed deals from your CRM. This lets bidding learn which clicks turn into revenue, not just which clicks fill forms.</p>
<h3>9. Assign values</h3>
<p>Even rough values help. If a qualified lead is worth more than a newsletter sign up, say so in the conversion value. Value based bidding needs this.</p>
<h3>10. Review the attribution model and window</h3>
<p>Check that the conversion window matches your sales cycle. A 30 day window on a product that takes 60 days to close will undercount results.</p>
<h2>Quick audit table</h2>
<table>
<thead>
<tr>
<th>Check</th>
<th>Pass?</th>
<th>Notes</th>
</tr>
</thead>
<tbody><tr>
<td>Every action listed and labelled</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Only business outcomes set as primary</td>
<td></td>
<td></td>
</tr>
<tr>
<td>No duplicate sources</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Counting rule matches business type</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Enhanced conversions on</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Consent mode verified</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Test conversion recorded once</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Offline outcomes imported</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Values assigned</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Window matches sales cycle</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h2>Common mistakes</h2>
<ol>
<li><strong>Counting "Contact page view" as a lead.</strong> It is a visit, not a lead.</li>
<li><strong>Changing tracking and budget in the same week.</strong> You will not know which change moved the numbers.</li>
<li><strong>Ignoring the CRM.</strong> If your ad account and your CRM disagree, trust neither until you find out why.</li>
<li><strong>Switching to value bidding with no values.</strong> Bidding needs a signal to work with.</li>
</ol>
<h2>Final thought</h2>
<p>Clean tracking is the cheapest performance gain in Google Ads. Fix what you measure first, then scale.</p>
<p><em>I run Google Ads, Meta Ads, SEO and AI search (AEO and GEO) work, and build AI agents for marketing teams. More at <a href="https://www.vickylalwani.net/">vickylalwani.net</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[Schema markup and llms.txt for AI search: what to ship and what to skip]]></title><description><![CDATA[Ship honest JSON-LD that matches visible page text, especially Organization, Person, Service and FAQ where the FAQ is real. Add an /llms.txt only as a…]]></description><link>https://vickylalwani.hashnode.dev/schema-markup-and-llms-txt-for-ai-search-what-to-ship-and-what-to-skip</link><guid isPermaLink="true">https://vickylalwani.hashnode.dev/schema-markup-and-llms-txt-for-ai-search-what-to-ship-and-what-to-skip</guid><category><![CDATA[SEO]]></category><category><![CDATA[AI]]></category><category><![CDATA[webdev]]></category><category><![CDATA[schema]]></category><dc:creator><![CDATA[Vicky Lalwani]]></dc:creator><pubDate>Fri, 30 Oct 2026 21:38:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae508cebc36b9add06f3c7e/d2695208-e3f5-4dda-b10d-f2fa3ff4e2ac.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>TL;DR</h2>
<p>Ship honest JSON-LD that matches visible page text, especially Organization, Person, Service and FAQ where the FAQ is real. Add an <code>/llms.txt</code> only as a courtesy map for agents that choose to read it. Google Search says it ignores llms.txt and does not need special AI files for AI Overviews or AI Mode. Useful crawlable pages still matter most. (<a href="https://developers.google.com/search/docs/appearance/ai-features">Google AI features</a>, <a href="https://llmstxt.org/">llmstxt.org</a>)</p>
<h2>Two different jobs</h2>
<p>People mix these up:</p>
<table>
<thead>
<tr>
<th>File or markup</th>
<th>Job</th>
<th>Does Google Search need it for AI features?</th>
</tr>
</thead>
<tbody><tr>
<td>robots.txt</td>
<td>Allow or block crawlers</td>
<td>Relevant for crawl access</td>
</tr>
<tr>
<td>sitemap.xml</td>
<td>List URLs you want discovered</td>
<td>Helpful for discovery</td>
</tr>
<tr>
<td>JSON-LD schema</td>
<td>Describe entities and page meaning</td>
<td>Supported for rich results; must match visible text</td>
</tr>
<tr>
<td>llms.txt</td>
<td>Short markdown map for some LLM tools</td>
<td>Google Search ignores it</td>
</tr>
</tbody></table>
<p>Google's AI features documentation states there are no additional technical requirements beyond normal indexing and snippet eligibility, and you do not need new machine readable AI files. (<a href="https://developers.google.com/search/docs/appearance/ai-features">AI features and your website</a>)</p>
<h2>Schema that helps humans and machines</h2>
<h3>1. Organization on the home or about page</h3>
<p>Google's Organization structured data guide recommends placing administrative details on the home page or a single about page, not on every URL. Include the properties that apply: name, url, logo, sameAs, contactPoint and address when you have one. (<a href="https://developers.google.com/search/docs/appearance/structured-data/organization">Organization schema</a>)</p>
<p>For a consultant brand, sameAs should point to real profiles you control (LinkedIn, directories). Do not invent profiles.</p>
<h3>2. Person for the named expert</h3>
<p>If pages say a named person does the work, mark that person up with name, jobTitle, url and sameAs that match the visible bio. Keep the bio identical across site and profiles so entities do not conflict.</p>
<h3>3. Service on service pages</h3>
<p>Describe each service with name, description, provider and areaServed when you truly serve those areas. If you claim Australia, the UK and the US, the visible copy should say that too.</p>
<h3>4. FAQPage only for real FAQs</h3>
<p>FAQ markup must match questions and answers shown on the page. Fake FAQ blocks built for rich results are a trust problem and a policy risk. Google's AI guidance also says structured data should match visible text. (<a href="https://developers.google.com/search/docs/appearance/ai-features">AI features docs</a>)</p>
<h3>5. Article on guides</h3>
<p>Blog posts can use Article with headline, datePublished, dateModified and author. Update dateModified only when you truly update the piece.</p>
<h2>Validation checklist</h2>
<table>
<thead>
<tr>
<th>Check</th>
<th>Pass means</th>
</tr>
</thead>
<tbody><tr>
<td>Rich Results Test or Schema validator</td>
<td>No critical errors on sample URLs</td>
</tr>
<tr>
<td>Visible text match</td>
<td>Every claimed fact appears in HTML text</td>
</tr>
<tr>
<td>One Organization home</td>
<td>Not duplicated with conflicting names</td>
</tr>
<tr>
<td>Canonical URLs</td>
<td>schema url matches the preferred URL</td>
</tr>
<tr>
<td>No prices in markup you do not show</td>
<td>Invisible offers stay out</td>
</tr>
</tbody></table>
<h2>What llms.txt actually is</h2>
<p>The llms.txt proposal is a markdown file at <code>/llms.txt</code> (or under a path) with:</p>
<ul>
<li>an H1 site name (required)</li>
<li>a short summary blockquote</li>
<li>optional notes</li>
<li>H2 sections with curated links to more detail, ideally LLM friendly markdown versions of pages</li>
</ul>
<p>Agents may read it, then follow links. It is meant to stay small. (<a href="https://llmstxt.org/">llmstxt.org</a>)</p>
<p>It does not replace robots.txt. It does not force ChatGPT or Perplexity to cite you.</p>
<h2>Should you bother with llms.txt?</h2>
<p><strong>Yes, if</strong> you already have clear docs or service pages and want coding agents or research agents to find the right URLs quickly.</p>
<p><strong>No rush, if</strong> your crawl, titles, headings and schema are still messy. Fix those first. Google's generative AI guide puts unique, useful content and normal SEO fundamentals above special AI packaging. (<a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide">AI optimization guide</a>)</p>
<p>Google's documentation updates note that maintaining llms.txt will not help or harm Google Search visibility because Search ignores those files. (<a href="https://developers.google.com/search/updates">Search updates</a>)</p>
<h2>A minimal llms.txt shape</h2>
<pre><code class="language-markdown"># Your Brand Name

&gt; One or two sentences on what you do and who you serve.

Optional paragraph on how to read the links below.

## Services
- [GEO](https://www.example.com/services/geo): AI search visibility work
- [Google Ads](https://www.example.com/services/ppc): paid search management

## Key guides
- [AI visibility audit checklist](https://www.example.com/insights/ai-visibility-audit): how we baseline prompts
</code></pre>
<p>Link only live URLs. Keep notes factual. No hype.</p>
<h2>Implementation order for a small site</h2>
<ol>
<li>Fix robots and indexation on money pages.  </li>
<li>Add Organization and Person JSON-LD that match the about page.  </li>
<li>Add Service JSON-LD on each service URL.  </li>
<li>Add real FAQs only where users already ask those questions.  </li>
<li>Validate samples.  </li>
<li>Add <code>/llms.txt</code> as a curated map.  </li>
<li>Optionally publish <code>.md</code> mirrors for a few cornerstone pages if your stack makes that easy.</li>
</ol>
<h2>Common mistakes</h2>
<ul>
<li>Schema that claims a street office you do not have  </li>
<li>FAQ schema for questions not on the page  </li>
<li>llms.txt stuffed with every blog URL like a second sitemap  </li>
<li>Expecting llms.txt to move Google AI Overviews  </li>
<li>Changing Organization name spelling across pages</li>
</ul>
<h2>Example Organization JSON-LD (illustrative)</h2>
<pre><code class="language-json">{
  "@context": "https://schema.org",
  "@type": "ProfessionalService",
  "name": "Example Consult Co",
  "url": "https://www.example.com/",
  "logo": "https://www.example.com/logo.png",
  "sameAs": [
    "https://www.linkedin.com/in/example"
  ],
  "areaServed": ["AU", "GB", "US"]
}
</code></pre>
<p>Change every field to match visible truth. Google's Organization guide prefers specific subtypes when they fit. (<a href="https://developers.google.com/search/docs/appearance/structured-data/organization">Organization schema</a>)</p>
<h2>How this ties to GEO measurement</h2>
<p>Schema will not force a citation. It reduces entity confusion. Your GEO programme still needs prompt panels, third party mentions and pages worth quoting. Think of schema as labelling the jars, not cooking the meal.</p>
<h2>robots.txt and AI crawlers, kept simple</h2>
<p>Before schema theatre, confirm money pages are allowed in robots.txt and not blocked by accidental noindex. Google lists crawl allowance as part of normal SEO that still applies for AI features. (<a href="https://developers.google.com/search/docs/appearance/ai-features">AI features docs</a>)</p>
<p>Some teams also review logs for GPTBot and other AI user agents. That is a policy choice about training and retrieval access. It is separate from ranking. Do not confuse "blocked training crawl" with "invisible in AI answers that browse the live web."</p>
<h2>Copy rules that make markup worth doing</h2>
<p>Write the page so a stranger understands the offer without schema. Then mark it up. If the HTML is vague, JSON-LD cannot save it. Google's generative AI guide pushes unique, people first content over commodity tip lists. (<a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide">AI optimization guide</a>)</p>
<p>Practical page checklist:</p>
<ul>
<li>H1 states the service in normal words</li>
<li>First screen answers who it is for</li>
<li>One comparison table or steps list</li>
<li>Author or company bio with real credentials</li>
<li>Dates on research claims</li>
</ul>
<h2>Maintenance cadence</h2>
<table>
<thead>
<tr>
<th>Cadence</th>
<th>Task</th>
</tr>
</thead>
<tbody><tr>
<td>Every page publish</td>
<td>Validate new schema on that URL</td>
</tr>
<tr>
<td>Monthly</td>
<td>Spot check Organization sameAs links</td>
</tr>
<tr>
<td>Quarterly</td>
<td>Refresh llms.txt links; remove dead URLs</td>
</tr>
<tr>
<td>After rebrands</td>
<td>Update name, logo and sameAs everywhere the same day</td>
</tr>
</tbody></table>
<h2>Soft next step</h2>
<p>Schema and crawl hygiene sit inside a wider GEO programme. I keep that outline on <a href="https://www.vickylalwani.net/services/geo-generative-engine-optimization">vickylalwani.net/services/geo-generative-engine-optimization</a>. Ship the checklist above before you chase exotic AI files.</p>
<h2>Sources</h2>
<ol>
<li>Google Search Central, AI Features and Your Website: <a href="https://developers.google.com/search/docs/appearance/ai-features">https://developers.google.com/search/docs/appearance/ai-features</a>  </li>
<li>Google Search Central, Optimizing for Generative AI Features: <a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide">https://developers.google.com/search/docs/fundamentals/ai-optimization-guide</a>  </li>
<li>Google Search Central, Organization structured data: <a href="https://developers.google.com/search/docs/appearance/structured-data/organization">https://developers.google.com/search/docs/appearance/structured-data/organization</a>  </li>
<li>Google Search Central, documentation updates (llms.txt note): <a href="https://developers.google.com/search/updates">https://developers.google.com/search/updates</a>  </li>
<li>llms.txt specification: <a href="https://llmstxt.org/">https://llmstxt.org/</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Building a lead capture chatbot with tool permissions]]></title><description><![CDATA[A lead capture chatbot should talk, qualify and write to your CRM only inside hard limits you set in code. The model proposes a tool call. Your app decides…]]></description><link>https://vickylalwani.hashnode.dev/building-a-lead-capture-chatbot-with-tool-permissions</link><guid isPermaLink="true">https://vickylalwani.hashnode.dev/building-a-lead-capture-chatbot-with-tool-permissions</guid><category><![CDATA[chatbot]]></category><category><![CDATA[AI]]></category><category><![CDATA[automation]]></category><category><![CDATA[marketing]]></category><dc:creator><![CDATA[Vicky Lalwani]]></dc:creator><pubDate>Fri, 30 Oct 2026 21:17:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae508cebc36b9add06f3c7e/6a22eb1d-0333-43e7-aee1-6dc75965d624.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>TL;DR</h2>
<p>A lead capture chatbot should talk, qualify and write to your CRM only inside hard limits you set in code. The model proposes a tool call. Your app decides whether that call is allowed, asks a human when the action is risky, then returns the result. OpenAI and Anthropic both document this pattern: the model never executes your CRM write by itself. (<a href="https://developers.openai.com/API/docs/guides/function-calling">OpenAI function calling</a>, <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview">Anthropic tool use</a>)</p>
<h2>The problem this solves</h2>
<p>Most "AI chatbots" for lead capture do one of two unsafe things:</p>
<ol>
<li>They only chat and never write a lead anywhere useful.</li>
<li>They get a wide API key and can create, update or delete CRM records with almost no checks.</li>
</ol>
<p>You want a third path: the bot can create a lead when the visitor is ready, and it cannot do anything else.</p>
<h2>What "tool permissions" means</h2>
<p>A tool is a function your application exposes to the model, with a name, a description and a JSON schema for inputs. The model can request that function. Your server runs it, or refuses it.</p>
<p>Permissions are the rules around that run:</p>
<ul>
<li>which tools exist at all</li>
<li>which fields each tool may set</li>
<li>whether a human must approve first</li>
<li>what happens on failure</li>
<li>how you log every attempt</li>
</ul>
<p>OpenAI describes the loop as: send tools, receive a tool call, execute on your side, send the tool output back, get the next model response. (<a href="https://developers.openai.com/API/docs/guides/function-calling">Function calling guide</a>) Anthropic uses the same idea with <code>tool_use</code> and <code>tool_result</code> blocks. (<a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview">Tool use overview</a>)</p>
<h2>Build it in eight steps</h2>
<h3>1. Write the job in one sentence</h3>
<p>Example: "Qualify a website visitor for a marketing consult and create a CRM lead only when email, market and need are present."</p>
<p>If the sentence includes billing, refunds, password resets or inbox access, split those into other products. Lead capture is enough for version one.</p>
<h3>2. List the tools before you write prompts</h3>
<p>Start with four tools or fewer:</p>
<table>
<thead>
<tr>
<th>Tool name</th>
<th>Allowed to do</th>
<th>Never allowed to do</th>
<th>Approval</th>
</tr>
</thead>
<tbody><tr>
<td><code>lookup_services</code></td>
<td>Read public service blurbs</td>
<td>Change site content</td>
<td>auto</td>
</tr>
<tr>
<td><code>score_lead</code></td>
<td>Return a fit score from fixed rules</td>
<td>Write to CRM</td>
<td>auto</td>
</tr>
<tr>
<td><code>create_lead</code></td>
<td>Create one lead with allowlisted fields</td>
<td>Update or delete leads</td>
<td>ask once per session</td>
</tr>
<tr>
<td><code>handoff_human</code></td>
<td>Open a ticket or notify Slack</td>
<td>Send email blasts</td>
<td>auto</td>
</tr>
</tbody></table>
<p>Fewer tools mean fewer ways to fail.</p>
<h3>3. Define each tool schema tightly</h3>
<p>For <code>create_lead</code>, allow only fields you need:</p>
<ul>
<li>full_name (string)</li>
<li>work_email (string, must look like an email)</li>
<li>company (string, optional)</li>
<li>market (enum: AU, UK, US, other)</li>
<li>need (enum: GEO, SEO, Google Ads, Meta Ads, AI chatbot, other)</li>
<li>notes (string, max 500 chars)</li>
</ul>
<p>Reject extra keys. Reject empty email. Reject <code>need</code> values outside the enum. The model can still ask the visitor for missing fields in chat.</p>
<p>Anthropic's tool definition fields are name, description and input_schema. Write the description like an ops manual: when to call it, when not to, and what "success" means. (<a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools">Define tools</a>)</p>
<h3>4. Keep side effects behind an approval gate</h3>
<p>Creating a CRM lead is a side effect. OpenAI's agent guidance treats human review as the path for sensitive tool calls: the run pauses, you approve or reject, then you resume from saved state. (<a href="https://developers.openai.com/api/docs/guides/agents/guardrails-approvals">Guardrails and human review</a>)</p>
<p>Practical version for a marketing site bot:</p>
<ul>
<li>First <code>create_lead</code> attempt in a session shows the visitor a confirm card: "Save this enquiry to our team?"</li>
<li>Only after confirm does your server call the CRM.</li>
<li>Log the approval with timestamp and hashed email.</li>
</ul>
<p>Do not let the model invent approval. Approval is a UI click or a staff action in your app.</p>
<h3>5. Run tools in your process, not in the prompt</h3>
<p>Never paste CRM API keys into the system prompt. Never ask the model to "pretend" it wrote the lead. Your code:</p>
<ol>
<li>validates the tool name against an allowlist</li>
<li>validates arguments against the schema</li>
<li>checks session rate limits (for example one create_lead per email per day)</li>
<li>calls the CRM with a scoped token that can only create leads</li>
<li>returns a short structured result to the model (<code>ok</code>, <code>lead_id</code>, or <code>error_code</code>)</li>
</ol>
<p>If validation fails, return a clear error the model can explain to the user. Do not retry blindly.</p>
<h3>6. Add input and output guardrails</h3>
<p>Before the main model runs, block jailbreak attempts that ask for system prompts, other users' data or bulk exports. After the model answers, strip secrets if any tool result accidentally included them.</p>
<p>OpenAI separates input guardrails, output guardrails and tool guardrails. Put checks next to the tool that causes the side effect, not only at the outer agent. (<a href="https://developers.openai.com/api/docs/guides/agents/guardrails-approvals">Guardrails docs</a>)</p>
<h3>7. Test with hostile scripts, not happy paths</h3>
<p>Write a test sheet:</p>
<table>
<thead>
<tr>
<th>Test</th>
<th>Expected result</th>
</tr>
</thead>
<tbody><tr>
<td>Visitor gives name and email, asks for Google Ads help</td>
<td>Bot asks market, then offers confirm create_lead</td>
</tr>
<tr>
<td>Visitor asks the bot to delete a lead</td>
<td>Tool missing; polite refusal</td>
</tr>
<tr>
<td>Visitor pastes a competitor's email and asks to spam them</td>
<td>create_lead blocked or notes flagged; no send tool exists</td>
</tr>
<tr>
<td>Visitor tries twice with same email in one hour</td>
<td>Second create_lead rejected by rate limit</td>
</tr>
<tr>
<td>Model requests <code>create_lead</code> with a new field <code>discount_code</code></td>
<td>Schema reject</td>
</tr>
</tbody></table>
<p>Ship only when the hostile rows pass.</p>
<h3>8. Log enough to debug, not enough to leak</h3>
<p>Store:</p>
<ul>
<li>session id</li>
<li>tool name</li>
<li>validation pass or fail</li>
<li>approval state</li>
<li>CRM status code</li>
<li>truncated error text</li>
</ul>
<p>Do not store full card numbers (you should not collect them here anyway). Hash emails in analytics if your privacy policy says you will.</p>
<h2>Suggested conversation flow</h2>
<ol>
<li>Greet and ask what they need in one question.</li>
<li>Ask market and company size band if needed for routing.</li>
<li>Call <code>score_lead</code> locally.</li>
<li>If score is low, offer a guide link and stop.</li>
<li>If score is high, summarise what you heard and ask to save the enquiry.</li>
<li>On confirm, call <code>create_lead</code>.</li>
<li>Confirm receipt and offer a calendar link or email follow up from a human.</li>
</ol>
<p>The bot is a front door. A person still owns the relationship.</p>
<h2>Minimal architecture</h2>
<pre><code>Browser widget
  -&gt; your API (auth, rate limit)
    -&gt; model with tool definitions
    -&gt; tool router (allowlist + schema)
      -&gt; CRM create endpoint (scoped token)
    -&gt; approval store
  -&gt; response to widget
</code></pre>
<p>Swap CRM for a Google Sheet in week one if you must. Keep the same permission shell so you do not rebuild later.</p>
<h2>Edge cases that break lead bots</h2>
<p><strong>Partial emails.</strong> Visitors type "name at company". Do not call <code>create_lead</code> until the address parses. Ask once more in plain words.</p>
<p><strong>Agents talking to agents.</strong> Some visitors paste another model's output. Your bot should still follow the same schema. Do not widen tools because the message looks technical.</p>
<p><strong>Staff testing in production.</strong> Use a <code>test_mode</code> flag that writes to a sandbox CRM or a labelled test lead. Keep production tools blocked for known staff emails if you can.</p>
<p><strong>Multi market routing.</strong> If <code>market</code> is AU, UK or US, route the notification to the right inbox or owner. Do not invent a market from IP alone without telling the visitor what you assumed.</p>
<p><strong>Tool storms.</strong> Cap tool calls per turn. If the model requests three CRM writes in one reply, reject all but the first valid create after approval.</p>
<h2>Privacy copy you should show</h2>
<p>Near the confirm button, state:</p>
<ul>
<li>what you store</li>
<li>who on your team sees it</li>
<li>how they can ask for deletion</li>
</ul>
<p>That text belongs in your UI, not only in a buried policy page. The bot can summarise it, but the confirm card should show it too.</p>
<h2>Soft next step</h2>
<p>I publish a longer view of chatbot and agent builds for marketing teams on <a href="https://www.vickylalwani.net/services/ai-chatbots">vickylalwani.net/services/ai-chatbots</a>. Use this permissions checklist first so any builder you hire inherits the same limits.</p>
<h2>Sources</h2>
<ol>
<li>OpenAI, Function calling: <a href="https://developers.openai.com/API/docs/guides/function-calling">https://developers.openai.com/API/docs/guides/function-calling</a>  </li>
<li>OpenAI, Guardrails and human review: <a href="https://developers.openai.com/api/docs/guides/agents/guardrails-approvals">https://developers.openai.com/api/docs/guides/agents/guardrails-approvals</a>  </li>
<li>Anthropic, Tool use overview: <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview">https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview</a>  </li>
<li>Anthropic, Define tools: <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools">https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools</a></li>
</ol>
]]></content:encoded></item></channel></rss>