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.
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.
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.
`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.
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.
`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.
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.