Fixing Contact Details That AI Agents Cannot Find or Read
One of the simplest jobs people hand to an AI assistant is "find me their number", "what time do they close on Saturday?" or "email them to ask about availability".
It fails more often than you would expect, because many sites keep contact details in a picture, behind a script that decodes an email address, inside a map embed or only in a chat widget. A person squints and finds it; an agent reading the page does not. This guide shows where contact details go missing, how to publish them as plain text and matching structured data, and how to keep spam under control while you do it.
What agent-readable contact details look like
The target is simple: every detail a customer might ask for is written as text in the HTML your server sends, in a predictable place, with links that do the obvious thing. A footer block like this covers most small businesses:
<footer>
<h2>Contact Hollybank Garden Rooms</h2>
<address>
Unit 4, Mill Lane Trading Estate<br>
Chesterfield<br>
S41 0AA<br>
Phone: <a href="tel:+441246000000">01246 000000</a><br>
Email: <a href="mailto:enquiries@example.co.uk">enquiries@example.co.uk</a>
</address>
<h3>Opening hours</h3>
<ul>
<li>Monday to Friday: 8am to 5:30pm</li>
<li>Saturday: 9am to 1pm</li>
<li>Sunday and bank holidays: closed</li>
</ul>
<p><a href="/contact">Contact page and enquiry form</a></p>
</footer>
A few points in that example are easy to get wrong:
- The
tel:link uses the international format,+44followed by the number without its leading zero and without spaces. The visible text can use the familiar local format. - The email appears twice in the
mailto:link, once in the address and once as visible text, so a reader and a parser both get it. - The
addresselement is meant for contact information about the page or site owner, which is exactly this use. Do not use it for other postal addresses in the content. - Hours are written in words a person would say. Avoid ambiguous formats like "8-5.30", and spell out closures for bank holidays if they differ.
The business name, the numbers and the address in this example are invented. Use your own, and keep them identical everywhere they appear.
Structured data that matches the page
Structured data gives agents the same details in a form they do not have to interpret. Use the most
specific LocalBusiness subtype that fits, such as HomeAndConstructionBusiness,
Dentist or Restaurant. Every value in the markup must also appear on the page,
and they must agree.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HomeAndConstructionBusiness",
"@id": "https://www.example.co.uk/#business",
"name": "Hollybank Garden Rooms",
"url": "https://www.example.co.uk/",
"telephone": "+441246000000",
"email": "enquiries@example.co.uk",
"address": {
"@type": "PostalAddress",
"streetAddress": "Unit 4, Mill Lane Trading Estate",
"addressLocality": "Chesterfield",
"postalCode": "S41 0AA",
"addressCountry": "GB"
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "08:00",
"closes": "17:30"
},
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": "Saturday",
"opens": "09:00",
"closes": "13:00"
}
],
"contactPoint": {
"@type": "ContactPoint",
"contactType": "customer service",
"telephone": "+441246000000",
"email": "enquiries@example.co.uk",
"areaServed": "GB",
"availableLanguage": "en"
}
}
</script>
Days not listed in openingHoursSpecification are read as closed, which is why Sunday
is missing. For holiday closures, add a separate specification with validFrom and validThrough
dates covering the closure, and update the text on the page at the same time.
The fix, step by step
Step 1
List where your contact details appear
Make a simple list of every place the phone number, email address, postal address and opening hours appear: the header, footer, contact page, individual service or branch pages, the Google Business Profile and any PDF menus or price lists. Note which version of each detail is shown in each place. Out-of-date numbers and old hours surviving on forgotten pages are common, and an agent may find the wrong one first. Decide on one correct set of details before changing anything.
Step 2
Check what the raw HTML contains
Run
the AI Agent Compatibility audit on your home page and contact page, then open the page source with view-source
or fetch it with curl -s and search it for your phone number and email address. If the number
is missing, it is in an image or added by script. If you see [email protected] or /cdn-cgi/l/email-protection,
your CDN is rewriting email addresses. Record what is missing on each page so you can check it again
afterwards.
Step 3
Replace images and scripts with real text
Replace any phone number or hours that exist only in an image with real text, styled
with CSS if the font matters. Remove scripts that assemble email addresses. If Cloudflare Email Address
Obfuscation is on, either switch it off in the Scrape Shield settings or wrap the public contact address
in <!--email_off--> and <!--/email_off--> comments so that address
is left alone. Put the full postal address as text next to any map embed rather than relying on the map.
Step 4
Make phone and email into proper links
Wrap each phone number in a tel: link written in international format, such
as tel:+441246000000, and each email address in a mailto: link whose visible
text is the address itself. Avoid click-to-reveal buttons that hide part of the number until pressed.
If you publish several numbers, label each one in text, such as sales, repairs or out of hours, so an
agent can pick the right one for the task it was given.
Step 5
Publish opening hours as text and markup
Write your opening hours as a short list in plain words on the contact page and in the footer, including the days you are closed and how bank holidays are handled. Remove hours that exist only in images, PDFs or third-party widgets. When hours change for a holiday or a season, update the page, the structured data and your Google Business Profile on the same day, and take temporary notices down once the dates have passed.
Step 6
Add matching LocalBusiness structured data
Add a JSON-LD block using the most specific LocalBusiness type that fits, with
telephone, email, a full PostalAddress and openingHoursSpecification,
and a ContactPoint if you have separate departments. Make every value identical to the visible
text. Test it with the Schema Markup Validator, then rerun the audit and fetch the page again with curl
to confirm the details are now in the HTML the server sends.
Dealing with spam once your details are visible
The usual reason for hiding an email address is fear of spam. It is a fair concern, but hiding the address from everyone is a heavy price, and modern spam filtering does most of the work anyway. Some better options:
- Use a role address such as enquiries@ or bookings@ for the website, separate from personal mailboxes, and filter it hard.
- Rely on your email provider's filtering. Microsoft 365 and Google Workspace filter most bulk spam without any help from obfuscation.
- Protect the form, not the address. If spam arrives through your contact form, add a honeypot field that is properly hidden from assistive technology and a server-side rate limit, rather than a visible puzzle for every visitor.
- Accept a little noise. A few spam emails a week is usually a smaller cost than customers, and their agents, failing to find you.
For phone spam, publishing a number does not make much difference to nuisance calls, which mostly come from random dialling. A published number that appears in structured data and your Business Profile does, however, make it easier for people to check that a call claiming to be from you is genuine.
Platform notes
WordPress
Contact details are often set in the theme customiser, a page builder header template or a "top bar" plugin. Check that these output text rather than an image, and look for anti-spam plugins that encode email addresses with script. SEO plugins such as Yoast and Rank Math can output organisation or local business markup; make sure only one plugin produces it, and that its phone and address match the footer.
Shopify
Many themes place contact details in a footer text block, which is fine. Problems come from announcement bar images and from contact pages that use a form app with no visible details. Add the details as text in a rich text section on the contact page, and check whether your theme or an app already outputs structured data before adding your own.
Multi-location businesses
Give each branch its own page with its own address, phone number and hours, and its own LocalBusiness
markup with a distinct @id. A single contact page listing ten branches in one table is hard
for an agent to connect to a question like "is the Derby branch open on Sunday?".
Keeping details correct
- One source of truth. Keep contact details in one place in your CMS or theme settings and output them everywhere from there, rather than typing them into each page.
- A calendar reminder before holidays to update hours on the site, the structured data and your Business Profile together.
- A quarterly check that fetches the home page and contact page with curl and searches for the phone number and email address.
- Retire old numbers carefully. When a number changes, search the whole site, including PDFs and old blog posts, for the old one.
1. Why can an AI assistant not find my phone number?
Usually because the number is not in the page as text. It may be part of a header image, added by a script after the page loads, or shown only after clicking a reveal button. Fetch-based agents read the HTML your server sends and see none of those. Check with view-source or curl: if you cannot find the number in the raw HTML, add it as plain text inside a tel link, then check the source again.
2. Does Cloudflare email obfuscation stop agents reading my email address?
It can. Cloudflare's Email Address Obfuscation rewrites email addresses in the page and uses JavaScript to decode them in the browser. A tool that reads the HTML without running scripts sees a placeholder and a link to an email protection page instead of your address. You can switch the feature off for the zone, or wrap your public contact address in email_off comments so Cloudflare leaves that one address untouched. Then check the page source again to confirm the real address now appears.
3. Should I put my email address on the site if I get lots of spam?
Usually yes, using a dedicated role address such as enquiries@ that you filter hard. Modern email services catch most bulk spam without the address needing to be hidden, and hiding it stops customers and their agents from contacting you directly. If most spam comes through your form rather than your inbox, protect the form with a hidden honeypot field and rate limits instead of hiding the address. Review the filter every few months so genuine enquiries are not caught by mistake.
4. What format should a UK phone number be in a tel link?
Use the international format with a plus sign, the country code 44 and the number without its leading zero, and no spaces, for example tel:+441246000000. The visible text can stay in the familiar local style, such as 01246 000000. This works for callers inside and outside the UK, on mobiles and on desktop calling apps, and it matches the telephone value you should use in your structured data. Use the same number everywhere so the page, the markup and your profile agree.
5. How do I show holiday opening hours to agents?
Update the visible hours on your contact page and footer, for example a short notice listing the dates and times, and add a separate OpeningHoursSpecification in your structured data with validFrom and validThrough dates covering the change. Update your Google Business Profile special hours on the same day. Remove the notice once the dates pass, because agents that find old holiday hours may tell customers you are closed. A reminder in your calendar makes this easy to remember.
6. Is a contact form enough on its own?
A form is useful, but many tasks people give agents are simpler: find the number, check whether you are open, send a quick email. If the only route is a form, the agent must fill it in, which depends on the form being well labelled and not blocked by a puzzle. Publish at least one phone number or email address as text alongside the form so there is always a direct route.
7. Do I need both visible text and structured data?
Yes. The visible text is what people read and what browser-based agents see on the screen. Structured data gives fetch-based tools and search features the same facts in a predictable format. The two should always match, since conflicting details make agents and search engines less confident about either version. Treat the markup as a copy of what the page already says, never as a place to add details the page does not show.
The AI Agent Compatibility audit tests your page for bot blocks, content hidden behind JavaScript, unlabelled forms and controls, navigation agents cannot follow and missing structured data, and links each finding to the guide that fixes it.
Open AI Agent Compatibility Fixes โ