KYA · Know Your Agent

Agents will run the internet.
Let them transact safely.

You have an API. Writ lets verified agents buy credits, top up accounts, and pay for usage on it — with a real, accountable human behind every request.

$ npx @writhq/demo
Run the demo Read the spec records, not rails — no custody, no money transmission
  Chain of agency resolve any action → the accountable human
ACTION LIABILITY signs authorizes issued by bears event Action account.refill $500.00 agt_ Agent treasury-bot Ed25519 · claude-code mnd_ Mandate scoped · signed revocable prn_ Principal KYC verified US · accredited bearer Liability accountable party disputes · recourse One signed assertion. One round trip. A resolvable, co-signed record of who is accountable.
The gap

Your API can't tell who is behind an agent's request.

It looks like a person, but it isn't — and nothing on the wire says whose agent it is, what it may spend, or who is accountable if it's wrong. So the safe answer is to block it, and real volume goes away.

?

Human or agent?

Nothing on the wire distinguishes the two.

?

Whose agent?

An anonymous API key is not a counterparty.

?

What authority?

This action, this amount, this platform, right now?

?

Whose liability?

If it goes wrong, who do you dispute with?

How it works

Three moves. One round trip at the edge.

Setup happens once. After that, every agent call is one signed assertion and one verify call that fails closed.

STEP 01

A human onboards and issues a mandate

Pass KYC once, register the agent's public key, grant narrow authority: this action, these caps, this platform, expiring — revocable in seconds.

prn_ → mnd_ · signed grant
STEP 02

The agent signs every request

The SDK (or the MCP server in Claude Code) holds the keypair and signs each call — action, amount, platform, nonce, timestamp — in an X-Passport header.

agt_ key · JWS · nonce + ts
STEP 03

Your API verifies in one call

One middleware line calls /v1/verify: signature, mandate, scope, live revocation, caps, KYC. Back comes allow/deny, the resolved chain, and a signed receipt.

plt_ → vrf_ · allow / deny · co-signable
Sequence diagram of the full interaction: the principal onboards and issues a mandate once; on every call the agent signs an assertion, the platform verifies it with Writ in one round trip, then executes and countersigns on allow — or returns a 403 with a reason on deny.
Setup once; then one signed assertion and one verify round trip per call. Protocol →
Integrate

Accept agents in about ten lines.

Drop one middleware in front of the routes agents call. It fails closed — and anonymous callers get a 403 KYA required with an onboarding link instead of a silent block.

server.tsplatform · @writhq/verify
import { requireKYA } from "@writhq/verify";

// Accept agent traffic — one round trip, fails closed.
app.post(
  "/v1/account/refill",
  requireKYA({ action: "account.refill" }), // ← the integration
  async (c) => {
    const { chain } = c.get("kya");   // resolved chain
    await accounts.refill(c.req.valid("json"));
    return c.json({ ok: true, principal: chain.principal });
  }
);
HTTP 403 · KYA required

No passport on this request.

The middleware never executes the action for an anonymous agent. It returns a structured block with a link to onboard — so the traffic you used to throw away comes back verified.

anonymous agent → 403 → onboard → mandate → verified buyer
The demo · one command

Watch an agent buy — and get stopped when it shouldn't.

treasury-bot holds one mandate at the Northbank sandbox: account.refill ≤ $1,000/tx, ≤ $2,500/week. Everything below is what npx @writhq/demo runs against production.

treasury-bot @ claude-code  ·  northbank.sandbox
$ passport present --action account.refill --amount 500
ALLOW$500.00 reason ok · receipt vrf_9c1… signed remaining this week $2,000.00
$ passport present --action account.refill --amount 2000
DENY$2,000.00 reason per_tx_cap · mandate cap $1,000 / tx
$ passport present --action account.refill --amount 900
ALLOW$900.00 reason ok remaining this week $1,100.00
$ passport present --action account.refill --amount 900
ALLOW$900.00 reason ok remaining this week $200.00
$ passport present --action account.refill --amount 900
DENY$900.00 reason period_cap · only $200.00 left this week
⟳ principal revokes mnd_tr7… in the dashboard — one click, propagates in < 2s
$ passport present --action account.refill --amount 500
DENY$500.00 reason mandate_revoked · live revocation check
$ curl -s -X POST northbank.sandbox/v1/account/refill -d amount=250 # no X-Passport header
403KYA required onboard → northbank.sandbox/kya/start
 

Every ALLOW returns the resolved chain and a JWS receipt the platform can countersign — a record signed by both sides.

Mandates

The grant is scoped, signed, and revocable.

One principal, one agent, specific actions with hard caps, an expiry, and a live status. Platforms read the attributes they need — never the identity documents behind them.

mandate.jsonsigned grant
{
  "id": "mnd_tr7…",
  "principal": "prn_a1…",
  "agent": "agt_9f…",
  "scopes": [{
    "action": "account.refill",
    "max_amount_per_tx": 100000,
    "max_amount_per_period": 250000,
    "period": "week",
    "currency": "USD",
    "platforms": ["plt_northbank"],
    "purpose": "trading-capital"
  }],
  "not_before": "2026-07-22T00:00:00Z",
  "expires_at": "2026-08-22T00:00:00Z",
  "status": "active",
  "issuer_sig": "<JWS by passport issuance key>"
}
max_amount_per_tx · per_period

Hard caps

Per-transaction and per-period ceilings. The passport keeps the counters server-side.

not_before · expires_at

Time-boxed

Authority is valid only inside a window. Outside it, the passport denies first.

status: active → revoked

Instant revocation

One click in the dashboard. Verify is a live check, so the next request denies.

selective disclosure

Attributes, not documents

KYC level, country, accreditation flag — never the underlying documents.

Where we sit
Category law

Records, not rails. We attest decisions; we never custody or move value.

Rails-neutral

x402 · AP2 · ACP interop. AP2 mandates map to ours 1:1 — the KYA layer any rail can call.

Regulatory posture

No custody, no money transmission. KYC through licensed vendors; attestation only.

Subject rights

FCRA-shaped by design. Access, correction, and dispute flows are first-class.

Standards path

W3C VC / JWS migration. Token format tracks the standard; migration is a serializer change.

FAQ

Regulatory posture, privacy, and who goes first.

01 Are you a money transmitter or custodian?
No. Writ is an attestation layer. We sell decisions and records, never movement of value — we never touch, hold, or route funds. KYC runs through licensed vendors behind a pluggable interface.
02 What does a platform actually see about the human?
Attributes, not identity documents: KYC level, country, and an accreditation flag — enough to decide, nothing more. Full identity is disclosed only when the mandate grants it or a lawful request compels it. Subject access, correction, and dispute flows are built in (FCRA-shaped).
03 Who integrates first?
Anyone selling usage or credits to agents: API platforms and credit top-ups, ad-budget refills, trading and prop platforms, exchanges and on-ramps, marketplaces. The refill primitive generalizes wherever agents move value into a platform.
04 Do you compete with x402 / AP2 / ACP payment rails?
No — we're the identity layer any of them can call. A rail moves the value; the passport answers whether the agent moving it is authorized and who is accountable. AP2's mandates and ours are meant to map 1:1.
05 How do agents hold keys today?
The agent runtime holds an Ed25519 keypair and registers the public key with us. MVP mandate issuance is custodial — we sign after the principal authorizes in the dashboard (the CA model). The documented upgrade path is principal-held keys and W3C Verifiable Credentials with identical semantics.
06 What's in the alpha, and what's deliberately out?
In: the chain model, sandbox KYC + stub sanctions, mandates with caps and revocation, the verify + countersign API, the Northbank demo, the MCP server, a CLI, and audit export. Documented but not built yet: real KYC vendors, principal-held keys/VCs, webhooks, multi-sig mandates, per-jurisdiction rulepacks, and agent reputation scores.
Alpha · live sandbox

Run the whole chain in one command.

No signup. It creates a principal, an agent and a mandate, then walks allow → replay-block → cap-deny → live revocation → anonymous 403 against the live sandbox.

$ npx @writhq/demo

11 passed of 11 · the passport runs at api.writhq.com · curl the 403 yourself on the Launch page.