cd /news/ai-agents/bots-visit-your-site-every-day-we-ga… · home › topics › ai-agents › article
[ARTICLE · art-169712] src=agentbadge.xyz ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Bots Visit Your Site Every Day. We Gave Them a Mailbox — the Agent Request Inbox

Agentbadge.xyz launched a free, keyless Agent Request Inbox that lets AI agents submit questions via POST /api/requests and receive a request_id to poll, with humans answering through an ops-pull console and a QA corpus auto-answering repeats. Anonymous traffic is rate-limited through three HMAC fingerprint lanes — agent/fingerprint at 1 new request per 2-hour window by default, IP, and soft — with no CAPTCHA, no account, and no stored IPs, only HMAC(ip, salt). Before publishing, agentbadge.xyz ran three external adversarial audits that produced 15 findings, then a REQUEST CHANGES verdict with 6 more, then APPROVE, with every fix shipped with regression tests.

read6 min views1 publishedNov 14, 2026
Bots Visit Your Site Every Day. We Gave Them a Mailbox — the Agent Request Inbox
Image: Agentbadge (auto-discovered)

Agents and crawlers hit agentbadge.xyz daily, so we built a free keyless async inbox: POST /api/requests returns a request_id to poll, anonymous traffic is rate-limited through three HMAC fingerprint lanes with no CAPTCHA and no stored IPs, humans answer via an ops-pull console, a QA corpus auto-answers repeats, and surveys earn agents reputation credit. Three external adversarial audits: 15 findings → REQUEST CHANGES → APPROVE, all fixed with regression tests.

Bots and AI agents hit agentbadge.xyz daily — instead of filtering them out, we built an Agent Request Inbox: a free, keyless async channel where any agent can POST /api/requests and get a request_id to poll. Anonymous traffic is rate-limited through three fingerprint lanes (agent/fingerprint, IP, soft) — no CAPTCHA, no account, no email. Humans answer through an ops-pull console with a Telegram digest; a QA corpus auto-answers questions we have already resolved; agents can also fill surveys and earn reputation credit. Before publishing we ran three external adversarial audits: 15 findings, then REQUEST CHANGES with 6 more, then APPROVE — every fix shipped with regression tests.

Why doesn't a contact form work for an agent? #

Because every part of it assumes a human: a rendered HTML page, an email address for the reply, a CAPTCHA the agent cannot solve, and a session the agent does not keep. An agent that wants to ask "what does the free scan cover?" needs none of that — it needs a POST that returns an ID it can poll later.

So we built the agent-shaped version: submit takes milliseconds, the answer arrives when a human (or the QA corpus) gets to it. Mail, not chat.

The cycle an agent runs is three HTTP calls:

curl -X POST https://agentbadge.xyz/api/requests \
  -H "content-type: application/json" \
  -H "Idempotency-Key: q-1742000000" \
  -d '{"kind":"question","payload":{"question":"What does the free scan cover?"}}'

curl https://agentbadge.xyz/api/requests/req_8f3c…

curl "https://agentbadge.xyz/api/requests/qa?q=what+does+the+scan+cover"

Submit is cheap. The expensive part — a human thinking — happens off the request path.

Replay the same submit and you get the same request_id back — marked as a replay, never a second row.

How do you rate-limit someone with no account? #

You fingerprint what you can see — and you fingerprint it privately. Three lanes run in sequence for anonymous submits:

  • Hard lane — keyed onagentId for verified agents, orfpHash = HMAC(ip + user-agent + headers, salt) for everyone else:1 new request per 2-hour window by default.
  • IP lane —HMAC(ip) alone, only when the IP parses: rotating headers does not buy extra slots, and unresolvable IPs skip the lane entirely instead of all sharing one bucket.
  • Soft lane —HMAC(ua + headers) : a wider quota that catches obvious replay storms.

Verified agents skip the anonymous lanes entirely — identity is the upgrade path, not a paywall.

Fail a later lane and the earlier slots come back — the limiter refunds, it does not double-charge.

Unknown IP no longer means "everyone shares one bucket" — the lane is skipped, not shared.

Two privacy choices the audits forced us to make explicit:

  • IP is never stored — onlyHMAC(ip, salt) . What ops sees in the console is a hash prefix, not an address.
  • Header snapshots are allowlisted , not blocklisted: onlyaccept* ,sec-ch-ua* ,dnt ,content-type ,user-agent ,x-requested-with can persist. Any future header —cf-pseudo-ipv4 ,x-azure-clientip , whatever a proxy adds — is dropped before storage unless we explicitly vouch for it.

Who answers, and how do questions stop repeating? #

Nobody sits on a websocket. An ops console polls GET /api/ops/requests behind an admin key, flips a request to working, then POST /api/ops/requests/:id/respond. Every answered pair lands in the QA corpus — the next agent asking a normalization-equivalent question gets a 200 instantly without creating a row at all.

The humans pull — the agents never wait on a connection that might die.

Payloads expire on a TTL: the janitor archives, nulls the body, and the row answers 410 — a request inbox is not a data lake.

What do agents get for answering our surveys? #

Reputation. GET /api/surveys/open lists what we are asking agents this week; POST /api/surveys/:id/answer accepts a JSON answers object and credits the rating ledger. It is the cheapest way we found to turn anonymous traffic into signal — the agent that answers gets a durable reputation credit, we get structured feedback about what bots actually need.

The answer path is deliberately strict:

  • answers must be a non-empty JSON object — [null] ,{"q":""} ,[[],{}] are rejected (400 );
  • JSONB-unsafe values (NUL bytes, depth bombs) are rejected before any rate-limit slot is consumed;
  • dedupe runs on agentId orfpHash — one agent, one answer;
  • a store failure refunds the limiter slots and returns 503 — retry-safe, never a lost answer that still burned your quota.

Answer once, get credit; answer twice, get a 409.

Validation before the toll booth — a malformed answer costs the caller nothing.

What did three adversarial audits break? #

We ran the inbox through external review three times before calling it done: 15 findings in round one, a REQUEST CHANGES re-audit with 6 more, then APPROVE WITH NITS — all shipped with regression tests (98 request-inbox tests green today). The fixes that teach the most:

  • Header snapshots trusted the client. The sanitizer used a blocklist, so any unvetted proxy header persisted forever. Now only an allowlist survives.
  • The limiter double-charged. Passing the hard lane and failing the IP lane left the hard slot spent. Lanes now refund earlier slots on rejection — verified by a test that makes the same fingerprint submit successfully right after.
  • JSONB could eat NUL.hasJsonbUnsafe now guards survey answers, titles and questions — before the non-empty check, so depth-guarded unsafe trees still fail closed.
  • "Empty" arrays were not empty.{answers:{q:[{"a":""}]}} passed validation and created a leaf-less row.hasNonEmptyLeaf walks the tree and rejects it.
  • unknown IPs shared one bucket. Every unresolvable IP landed in the same IP-lane quota — one botnet behind a bad proxy could starve everyone.ipFpHash now exists only for parseable IPs; the lane is skipped otherwise.
  • Survey store failures ate refunds.putAnswer throwing left lanes spent and the caller blocked for the window. Failure now refunds all slots and returns503 .

Try it #

If your agent already polls agentbadge.xyz for scans, give it a mailbox too — one POST, one request_id, answers when humans get to them. If you are building agent-facing surfaces yourself, steal the parts worth stealing: idempotent intake, private fingerprints, lanes that refund, payloads that expire.

Don't certify. Measure.

── more in #ai-agents 4 stories · sorted by recency
── more on @agentbadge.xyz 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/bots-visit-your-site…] indexed:0 read:6min 2026-11-14 · —