/ Agent Compat Fixes / Agent Forms

How to Fix Forms AI Agents Can't Fill

When an AI agent tries to book your demo, request a quote, or sign a user up, it needs to identify each field and submit valid data. Forms with random field IDs, no labels, hidden honeypots that look like real fields, or JS-only validation all break agents. The fix is largely the same as making forms accessible to screen readers — labels, semantic IDs, and proper autocomplete. This guide covers the agent-friendly form pattern.

1. The single principle

If a screen reader can identify and submit your form, an agent can too. Agents and screen readers both read the DOM looking for label text and ARIA — neither sees visual layout. Accessibility-equals-agent-friendly.

2. Required form basics

Every input needs a label

<!-- BAD: placeholder as label -->
<input type="email" placeholder="Email" />

<!-- GOOD: programmatic label -->
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" />

<!-- ALSO GOOD: aria-label if visual label undesired -->
<input type="search" aria-label="Search products" />

Stable, semantic IDs

<!-- BAD: random ID, regenerated each render -->
<input id="input-x7k2p9" name="firstName" />

<!-- GOOD: semantic, stable ID -->
<input id="first-name" name="firstName" autocomplete="given-name" />

Autocomplete attributes

Single most underused agent hint:

autocomplete="given-name"      → First name
autocomplete="family-name"     → Last name
autocomplete="email"           → Email
autocomplete="tel"             → Phone
autocomplete="street-address"  → Street
autocomplete="postal-code"     → Postcode / ZIP
autocomplete="cc-number"       → Credit card
autocomplete="organization"    → Company
autocomplete="url"             → Website

3. Anti-spam without breaking agents

Honeypot fields done right

<!-- BAD: looks like a real field but is a trap -->
<input type="text" name="email" style="display:none" />
<input type="text" name="email_real" />
<!-- Confusing for agents and screen readers -->

<!-- GOOD: clearly named honeypot, hidden via CSS, labelled as such -->
<div class="hp-wrap" aria-hidden="true" style="position:absolute;left:-9999px">
  <label for="website-extra">Leave this blank</label>
  <input id="website-extra" name="url_honeypot" type="text" tabindex="-1" autocomplete="off" />
</div>
<!-- Real users skip via aria-hidden + tab-index; bots fill it -->

Captcha — when to deploy

Captcha-always blocks both spammers and agents indiscriminately. Captcha-on-suspicion is better:

// Only show captcha if behaviour suggests bot
if (submitTimeMs < 1500 || honeypotFilled || abuseScoreHigh) {
  return requireCaptcha(req, res);
}
// Otherwise pass through

4. Multi-step forms

Use real URLs per step

<!-- BAD: client-side step state, single URL -->
/checkout
  ↓ (JS state change, URL doesn't change)
/checkout (now showing step 2)

<!-- GOOD: URL per step -->
/checkout/details
/checkout/shipping
/checkout/payment
/checkout/review

Agents can resume any step by URL. Refresh-safe. Bookmarkable.

Progress visible in HTML

<nav aria-label="Checkout progress">
  <ol>
    <li aria-current="step">Details</li>
    <li>Shipping</li>
    <li>Payment</li>
  </ol>
</nav>

5. Validation messages

<!-- Connect errors to fields programmatically -->
<label for="email">Email</label>
<input id="email" name="email" type="email" 
       aria-invalid="true" aria-describedby="email-error" />
<div id="email-error" role="alert">
  Please enter a valid email address.
</div>

<!-- Agent reads aria-describedby, knows what's wrong, fixes input -->

6. CSRF tokens

<!-- Render in initial HTML, not via JS -->
<form action="/api/submit" method="POST">
  <input type="hidden" name="_csrf" value="{{csrfToken}}" />
  <!-- ... fields ... -->
</form>

<!-- Agent extracts token from HTML and includes in submission -->

7. Test with an agent simulation

Step 1
Playwright with label selectors
// If this works, agents will too
import { test } from '@playwright/test';

test('book demo form', async ({ page }) => {
  await page.goto('https://example.com/demo');
  await page.getByLabel('First name').fill('Test');
  await page.getByLabel('Email').fill('test@example.com');
  await page.getByLabel('Company').fill('Acme');
  await page.getByRole('button', { name: 'Book demo' }).click();
  await page.waitForURL('**/thanks');
});
Step 2
Try ChatGPT-User
Ask ChatGPT (with browsing enabled) to "book a demo on example.com for John at Acme". Watch if it can complete the form. If it stalls or asks for clarification on field purpose, your labels/autocomplete are weak.
💡 Agent-friendly forms also convert better for humans. Clear labels reduce abandonment. Proper autocomplete saves typing on mobile. Semantic IDs and ARIA help screen readers. The agent-compat work pays for itself in human metrics.

🤖 Re-run Agent Compat audit

Verify forms agent-friendly.

Run Agent Compat →
Related Guides: Agent Compat Fixes  ·  Fix Agent Checkout  ·  Fix Form Labels
💬 Got a problem?