For services

The next user signing up to your service
isn’t a human.

Treat AI agents as first-class customers — not bots to block. Add AFAuth to your API in five lines and agents sign themselves up with a cryptographic identity, a verified human on the hook, and an audit trail built in. Every signed call is revenue your competitors are still blocking.

Integrate

Add AFAuth to your API. Once.

Five lines of TypeScript in any HTTP framework — or one file on Cloudflare Workers. From then on, any agent with the afauth CLI can sign itself up and call you.

$ npm i @afauthhq/server
server.ts @afauthhq/server
import { defineService } from '@afauthhq/server';

const server = defineService({
  baseUrl, serviceDid,
  accounts, recipients,
});
// mount server.handler on your router

Cryptographic identity, signed requests, owner-claim invitations, and per-account revocation — handled. On Cloudflare? @afauthhq/worker ships the same thing for the edge: D1 for accounts, KV for nonces, one Worker file.

The dial

Decide how strict. One flag.

Attestation is on by default — a verified human stands behind every signup. Turn it down only where spam isn't the threat.

Required · default

recommended

A human on the hook for every signup.

Spam-resistant out of the box. Quotas key off sub_h, so one human is one free tier — however many agents they run.

defineService({
  …,
  attestation: 'required', // default
})

Off

max reach

Pure keypair. No human signal.

For read-only or paid-only APIs where abuse isn't the concern. Anyone with a key is in. There's an 'optional' middle ground too — attest if you can, allow if you can't.

defineService({
  …,
  attestation: 'off',
})

Agent-first? Go further

Ship your own agent — and onboard it with zero friction.

If you distribute your own agent or CLI, bundle the afauth client so it signs itself up the moment it runs — no signup screen, no key paste. Then list your endpoint in the registry so every other agent can discover you too.

The trust layer

A human signal.
Without a human profile.

Free tiers and rate limits leak when "user" means "public key." trust.afauth.org issues short-lived, offline-verifiable per-service pseudonyms. Same human, same bucket here. Different service, different string.

The gap

Ten thousand keypairs. Ten thousand free tiers.

A public key is cheap. To your quota table, a bad actor spinning up throwaways looks like a thousand new customers — one bucket each.

service-policy ?
if (account.isNew())
  return FREE_TIER;
// …10,000 times over.

The signal

A pseudonym. Scoped to the human.

A short-lived JWT carries sub_h — a stable, opaque handle for the human behind this agent, scoped to your service alone. Two services compare notes? They see different strings.

attestation verifies offline
verification: "email"
aud:          did:web:your.app
sub_h:        qN3Z9k…vM2
exp:          15m

The outcome

One human. One bucket.

Key your free-tier limits, rate limits, and bans off sub_h instead of the DID. All of one human's agents collapse into one bucket. Per-DID revocation still handles the finer cases.

server.ts 1 human / 1 bucket
const bucket = quota.get(claims.sub_h);
if (bucket.exhausted)
  return 429;

Be the first to serve the next million agents.

Five lines turns your API into something agents can sign up to on their own. List it in the registry and they'll find you.