A practical guide for agencies and developers.
We’ll set up four things: llms.txt, an agent-card.json, JSON-LD schema, and a contact form an AI agent can actually submit.
More traffic to your site is going to come from AI agents acting on behalf of humans — ChatGPT browse mode, Perplexity, Claude for Chrome, custom-built agents. If your site is built purely for human eyeballs, you're becoming invisible to them.
At MoyaHost we build AI chatbots, voice agents, and workflow automations — so running a site that isn't agent-ready wasn't an option. This post is the exact setup we deployed, including the mistake that cost me a rewrite.
Starting point: a working WordPress site with a functioning contact form that sends email notifications. If you don't have that, fix it first.
Our stack: WordPress on cPanel, Elementor, Fluent Forms (free), Fluent SMTP (free) → Microsoft 365, Code Snippets, Cloudflare DNS.
Step 1: Use a native WordPress form, not Typeform
Typeform embeds are iframes from `typeform.com`. For an agent to submit one, it has to drive a browser — slow, flaky, and not documentable in a standard. A native form lives on your own domain with a submission endpoint you control and can describe. We picked Fluent Forms. WPForms, Gravity Forms, or Contact Form 7 all work too; the endpoint details below will differ. Build the form, replace your Typeform embed with the shortcode (`[ fluentform id= "1" ]`), and confirm it renders.
Step 2: Reliable email delivery (Microsoft 365 + OAuth)
WordPress's default `wp_mail()` is notoriously unreliable on shared hosts. You need SMTP. Cloudflare is not an SMTP provider. This catches people out. Cloudflare does inbound Email *Routing* (forwards incoming mail to your inbox), and has an Email *Sending* product in private beta — but nothing usable for a WordPress form today. If you already pay for Microsoft 365, use it. No extra service, no extra cost, emails come from the real mailbox. Critical 2026 timing note: Microsoft has deprecated Basic Authentication for SMTP. After April 30, 2026, username-and-password SMTP to Office 365 stops working. You must use OAuth 2.0 via Microsoft Graph. Fluent SMTP handles this natively. The setup: 1. In Microsoft Entra → App registrations → New registration — multi-tenant, use the callback URL Fluent SMTP shows you. 2. API permissions — add Microsoft Graph delegated permissions: `SMTP.Send`, `Mail.Send`, and `offline_access`. Without `offline_access`, tokens expire after an hour. 3. Grant admin consent for all three. 4. Certificates & secrets → New client secret — set to 24 months (the max) and calendar-reminder the renewal. 5. 'Web' redirect URI : https://moyahost.ai/wp-json/fluent-smtp/outlook_callback 6. Plug the Application ID and Client Secret Value into Fluent SMTP and authenticate. In Fluent Forms' notification settings, set `From Email` to the mailbox your app has access to (e.g. `[email protected]`), and `Reply-To` to `{inputs.email}`. Without Reply-To, hitting Reply in Outlook sends back to yourself.
Step 3: The llms.txt and llms-full.txt files (the AI-SEO layer)
`llms.txt` is a 2024 proposal by Jeremy Howard (Answer.AI): a curated, markdown-formatted list of your site's important pages at the domain root, so LLMs can understand your site without crawling full HTML.
Example:
# MoyaHost
> MoyaHost B.V. is a Netherlands-based AI automation agency building
> custom AI chatbots, voice agents, and workflow automations for
> SMBs in NL and the EU.
## What we do
- **AI chatbots** — conversational agents tailored to your business
- **Voice agents** — AI phone/voice interfaces
- **Workflow automation** — AI integrated into existing processes
- **Consultancy** — ROI-focused AI advisory
- **Training** — team workshops on building with AI
## Key pages
- [Home](https://moyahost.ai/): Overview of services
- [Videos](https://moyahost.ai/videos/): Demos of chatbots and voice agents
- [Contact](https://moyahost.ai/contact-us/): Request a consultation
## Contact
- Email: [email protected]
- Response time: Within 1 business day
`llms-full.txt` is the same idea but with full page content inlined — useful for agents that want deep context in one fetch.
Upload both to the root of your WordPress install via cPanel File Manager. They must sit at `https://yourdomain.com/llms.txt`, not in a subdirectory.
Exclude them from your XML sitemap. Sitemaps are for search engines indexing HTML pages; including `.txt` files risks duplicate-content flags. Yoast and Rank Math exclude them by default — verify.
Honest caveat: ChatGPT, Claude, and Gemini don't automatically fetch `llms.txt` during inference today. It's a bet on where the ecosystem is going. Adoption is accelerating (Anthropic, Stripe, Cloudflare, Perplexity have all shipped one), but don't expect immediate SEO gains from this file alone.
Step 4: The agent card (where I got it wrong the first time)
The promise: an agent reads a machine-readable file describing how to submit your contact form, then POSTs on behalf of a user. I assumed Fluent Forms exposed a REST endpoint at `/wp-json/fluentform/v1/forms/{id}/submit`, wrote an agent card documenting JSON POSTs to that path, and deployed it. = 404.
Hitting `https://moyahost.ai/wp-json/fluentform/v1/` in a browser revealed the actual REST routes. There was no public `/submit` endpoint. Fluent Forms' REST API is for admin form management only — not public submissions.
Lesson: test your own agent card before deploying it. A card documenting a fictional endpoint is worse than no card.
How Fluent Forms actually works
Submissions go through WordPress's legacy AJAX endpoint:
- URL: `/wp-admin/admin-ajax.php`
- Content-Type: `application/x-www-form-urlencoded` (not JSON)
- Body: `action=fluentform_submit&form_id=1&data=<url-encoded-query-string-of-fields>`
Yes — url-encoding inside url-encoding. It's a pre-REST-era WordPress pattern.
One good-news finding: Fluent Forms' nonce check is lenient by default. Their source reads:
// Validate the form nonce. By default, it does not verify
// as many people use caching
$this->validateNonce();
So a capable agent can submit in a single POST without scraping a nonce first.
The working agent card
Drop this at `/.well-known/agent-card.json`:
{
"name": "MoyaHost",
"description": "Netherlands-based AI automation agency. This is a business contact card, not a live A2A server.",
"url": "https://moyahost.ai",
"contact": {
"email": "[email protected]",
"form_url": "https://moyahost.ai/contact-us/",
"response_time": "1 business day",
"submission": {
"method": "POST",
"endpoint": "https://moyahost.ai/wp-admin/admin-ajax.php",
"content_type": "application/x-www-form-urlencoded",
"request_parameters": {
"action": { "constant_value": "fluentform_submit" },
"form_id": { "constant_value": "1" },
"data": {
"description": "URL-encoded query string of form fields",
"fields": {
"names[first_name]": { "type": "string" },
"names[last_name]": { "type": "string" },
"email": { "type": "string", "required": true },
"checkbox[]": {
"type": "array of string",
"allowed_values": [
"AI Chat Bots", "AI Voice Agents", "AI Workflow",
"AI Consultancy", "AI Training"
]
},
"message": { "type": "string", "required": true }
}
}
}
}
}
}
Field names must match the exact `name` attributes in your rendered form HTML. Confirm by viewing source on the live contact page — if you rename a field and forget to update the card, submissions silently fail.
If the `.well-known` folder doesn't exist, create it. cPanel File Manager may hide it — enable "Show Hidden Files" in Settings.
Step 5: JSON-LD (the part that pays off today)
`llms.txt` and `agent-card.json` are bets on the future. **JSON-LD is consumed right now** by Google AI Overviews, Perplexity, ChatGPT browse mode, and the whole structured-data ecosystem. If you do only one thing from this post, do this. Paste into your homepage `<head>` via Code Snippets (HTML snippet, Site Wide Header):
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [{
"@type": "Organization",
"@id": "https://moyahost.ai/#organization",
"name": "MoyaHost",
"legalName": "MoyaHost B.V.",
"url": "https://moyahost.ai",
"email": "[email protected]",
"areaServed": [
{ "@type": "Country", "name": "Netherlands" },
{ "@type": "Place", "name": "European Union" }
],
"knowsAbout": [
"AI chatbots", "Voice AI agents", "AI workflow automation",
"AI consultancy", "AI training"
],
"contactPoint": {
"@type": "ContactPoint",
"contactType": "sales",
"email": "[email protected]",
"url": "https://moyahost.ai/contact-us/",
"availableLanguage": ["English", "Dutch"]
}
}]
}
</script>
<link rel="alternate" type="text/plain"
href="https://moyahost.ai/llms.txt" title="LLM site index" />
Validate at `https://search.google.com/test/rich-results`. `Organization` should parse with zero errors.
The `<link rel="alternate">` tag is the official llms.txt discovery mechanism — tells agents reading your homepage where to find your LLM index.
Step 6: Test end-to-end
The step most people skip and the most important one. A deployed setup you haven't tested is worse than useless. From PowerShell (using `Invoke-RestMethod` to avoid TLS quirks with Windows curl):
Invoke-RestMethod `
-Uri "https://moyahost.ai/wp-admin/admin-ajax.php" `
-Method Post `
-Body @{
action = "fluentform_submit"
form_id = "1"
data = "names%5Bfirst_name%5D=Aria&names%5Blast_name%5D=TestAgent&email=YOUR-EMAIL%40gmail.com&checkbox%5B%5D=AI+Chat+Bots&checkbox%5B%5D=AI+Workflow&message=Agent+test+submission"
}Successful response:
success data
------- ----
True @{insert_id=3; result=; error=}
Verify three things:
1. Email arrives with all fields populated correctly (including multi-select checkboxes).
2. Reply-To works — hitting Reply in your mail client puts the submitter's email in To:, not yours.
3. The entry appears in Fluent Forms → Entries with all checkboxes ticked.
If any of those fail, you have a silent bug to hunt.
Lessons worth writing down
- Test the documented endpoint before deploying the card. If your own simulated-agent test can't submit, neither can a real agent. - Nonces, rate limiting, and reCAPTCHA are the real blockers. Use honeypot-based anti-spam instead — reCAPTCHA blocks agents too. - Name your tools publicly. Hiding your plugin stack doesn't make you more secure (view-source reveals it in 10 seconds) but does make your docs useless. - Review quarterly. A stale card is worse than no card. If you change the form, update the card. - `.well-known/` can need server config. On Apache check `.htaccess`; on nginx you may need an explicit `location ^~ /.well-known/` block.
Realistic expectations
As of April 2026, no major AI product auto-discovers `agent-card.json`. What you've built pays off when: - A user points an agent at your URL explicitly - A developer builds an agent against your public docs - Future mainstream agents add auto-discovery (the incentive is enormous — they will) - Your JSON-LD gets picked up by Google AI Overviews and Perplexity *today* In 2015, mobile-responsive was optional. By 2018, not having it was disqualifying. Web-standard transitions from "nice to have" to "table stakes" are fast. Better to build this now than scramble in 2027.
Need help?
We build this infrastructure for clients — not just the files, but the thinking about how your business should be represented to AI. If you're an small/medium business wanting to be findable by AI agents, an agency looking for a technical partner, or curious about chatbots, voice agents, or workflow automation: [moyahost.ai/contact-us/](https://moyahost.ai/contact-us/) or [[email protected]](mailto:[email protected]). Human replies within one business day. If your agent submits our form, we'll know it was you.