# Letter of Agency API — loa/0.1

**Status: EXPERIMENTAL — a first try, for discussion. Not a standard, not stable, expect breaking changes.**
**DRAFT — prepared Sat Nov 14, 2026, overnight, for Jane Cates to approve/edit. Nothing published.**

Concept: Jane Cates, "The Letter of Agency: Verifiable Delegation for Personal AI Agents" (white paper, Nov 13, 2026).

## 1. What this is

A minimal HTTP API by which:

- an **agent** presents a **Letter of Agency** to a **verifier** (a site/service — e-commerce first),
- the verifier checks it and issues a narrow, one-errand grant — or a plain refusal,
- the actions taken are receipted back against the Letter, auditable by the **principal (durable owner)**,
- and the principal can revoke at any time.

It implements the white paper's **Present – Verify – Receipt** protocol. Trust attaches to the accountable owner and the chain of delegation — never to a session, cookie, or browser fingerprint.

## 2. The Letter

A Letter is a JSON object carrying the paper's **four verifiable claims**:

| Claim | Field(s) |
|---|---|
| Who I am (agent identity) | `agent`, signed by the agent's platform (`iss`) |
| Whose I am (delegation) | `principal`, `delegation` (principal's signature over agent + intent + audience + expiry) |
| What I am here to do (intent) | `intent` — plain language **and** machine-readable scopes, plus `limits` |
| How long it lasts (validity) | `iat`, `exp`, `aud` (audience binding), `jti` (unique Letter ID) |

Dual-signed: once by the agent platform (agent identity), once by the principal (delegation). A forged or altered Letter fails verification.

### Illustrative example Letter — ILLUSTRATIVE ONLY, not a normative schema

Consistent with the white paper's Appendix A, expanded only for readability:

```json
{
  "loa": "0.1",
  "iss": "agent-platform.example",
  "agent": "agent:muldoon-0533",
  "principal": "principal:pseudonymous-id-7f3a",
  "delegation": "sig(principal, agent + intent + aud + exp)",
  "intent": {
    "plain_language": "Check the status of one order",
    "scopes": ["order_status"]
  },
  "limits": { "purchase": false, "account_changes": false },
  "aud": "verifier.example",
  "iat": "2026-11-13T17:00:00-05:00",
  "exp": "2026-11-13T18:00:00-05:00",
  "jti": "letter-2026-11-13-0001",
  "signature": "sig(agent-platform, letter)"
}
```

Notes:

- `principal` is a pseudonymous-but-accountable identifier — resolvable by the issuing platform under due process, so a Letter does not broadcast the owner's legal identity to every site (white paper §6).
- Expiry in this example is 1 hour; minutes-to-hours is the intended range. A Letter is for **one errand**, not a standing pass.

## 3. Discovery

A verifier that accepts Letters publishes:

`GET /.well-known/letter-of-agency`

```json
{
  "loa_versions": ["0.1"],
  "intents_supported": ["order_status", "account_lookup"],
  "present_endpoint": "https://verifier.example/loa/0.1/present",
  "receipts_endpoint": "https://verifier.example/loa/0.1/receipts",
  "revocation_endpoint": "https://verifier.example/loa/0.1/revoke",
  "agent_platform_keys": "https://verifier.example/loa/0.1/jwks-or-trusted-issuers",
  "max_letter_lifetime_minutes": 120
}
```

No endpoint at that path = the site does not accept Letters (yet). Agents must not present Letters to sites that have not opted in.

## 4. Present

`POST /loa/0.1/present` — the agent presents its Letter **before acting**, in the open, not disguised as a browser.

Request body: the Letter JSON (§2), over TLS.

## 5. Verify

On receiving a Letter, the verifier checks, in order:

1. **Signatures** — platform signature valid for the agent claim; principal signature valid for the delegation claim (keys discovered via the issuer's well-known endpoint / registry — see Open Questions).
2. **Audience** — `aud` names *this* verifier. A Letter for another site is refused.
3. **Expiry** — `exp` not passed; lifetime within the verifier's stated maximum.
4. **Replay** — `jti` not seen before (verifier records `jti` until `exp`; see §8).
5. **Revocation** — the Letter / delegation is not on the revocation status the verifier checks.
6. **Scope** — the requested action falls **inside** `intent.scopes` and `limits`. Anything outside is refused, not partially trusted.

### Grant (success)

A narrow, one-errand grant, bound to the Letter:

```json
{
  "grant": "grant-token-opaque",
  "letter_id": "letter-2026-11-13-0001",
  "scopes_granted": ["order_status"],
  "expires_at": "2026-11-13T18:00:00-05:00"
}
```

- The grant inherits the Letter's expiry or is shorter — never longer.
- The grant authorises only the granted scopes, only at this verifier.
- The agent then makes its ordinary API calls for that errand, presenting the grant — it does not crawl, browse, or improvise outside the errand.

### Refusal (failure) — plain, machine-readable, final for that Letter

```json
{ "error": "scope_exceeded", "letter_id": "letter-2026-11-13-0001" }
```

Error values for v0.1: `signature_invalid`, `audience_mismatch`, `letter_expired`, `letter_replayed`, `letter_revoked`, `intent_not_supported`, `scope_exceeded`.

A refusal is not an invitation to retry disguised as a human or a browser. The agent reports the refusal to its principal.

## 6. Receipt

Every action taken under a grant is logged against the Letter ID.

`POST /loa/0.1/receipts` (verifier → agent platform / principal's audit log), and the principal can read receipts for their Letters:

```json
{
  "letter_id": "letter-2026-11-13-0001",
  "action": "order_status",
  "detail": "Order status read: shipped",
  "at": "2026-11-13T17:12:04-05:00",
  "verifier": "verifier.example"
}
```

The principal's view, in the paper's words: *"Your agent, acting under Letter #…, did X at time T."*

## 7. Revoke

`POST /loa/0.1/revoke` — by the principal (via their platform), naming a `letter_id` or the whole delegation.

- Revocation voids the Letter and any live grant under it.
- Verifiers check revocation status at Verify time (§5, step 5). How fast revocation propagates is an open question (§9) — short Letter lifetimes are the v0.1 safety net.
- An agent that acts outside its declared intent **voids its own Letter** — verifiers should treat out-of-scope action as grounds to revoke the grant and report the agent's platform.

## 8. Intent vocabulary — v0.1 (read-only only)

Start deliberately tiny. Expressiveness is worthless if scopes go so broad they mean nothing (paper §9).

| Intent | Meaning | Limits |
|---|---|---|
| `order_status` | Read the status of **one named order** belonging to the principal | Read-only; order identified in the request, not enumerated |
| `account_lookup` | Read basic account facts the principal could see themselves (e.g. account standing, saved addresses on file) | Read-only; no changes, no credential or payment-instrument details |
| `order_history_summary` | Read a summary list of the principal's recent orders | Read-only; summary fields only |

That's the whole v0.1 vocabulary. New intents arrive by versioning the vocabulary, not by stretching these.

## 9. Non-goals for v0.1 (explicit)

- **No payments, no purchases** — no scope in v0.1 can move money (the paper's specimen Letter has `purchase: false`; purchase intents with price ceilings are a later version's problem)
- **No account changes** — no edits, no settings, no address changes
- **No messaging or posting** on the principal's behalf
- **No open-ended access** — no "browse my account", no general browsing/scraping permissions, no standing grants
- **No cross-site reuse** — one Letter, one audience, one errand
- **No human impersonation** — Letters exist precisely so agents stop disguising themselves as browsers

## 10. Security notes

- **Replay:** `jti` is unique per Letter; verifiers store seen `jti`s until the Letter's `exp` and refuse repeats. Grants are single-errand and short-lived.
- **Expiry:** minutes to hours, single-purpose. A stolen Letter is useless outside its audience and its window (audience binding + short life are the mitigation, per paper §6).
- **Audience binding:** `aud` is checked before any scope is honoured — a Letter captured at one site cannot be replayed at another.
- **Forged Letters:** dual signatures; verification uses the issuer platform's published keys and the principal's account attestation. A broken chain = refusal.
- **Confused principals:** the signing flow must show the intent in plain language at signing time; v0.1's read-only vocabulary keeps the blast radius of a tricked signature small.
- **Transport:** TLS everywhere; Letters and grants are bearer-sensitive — never log full Letters at the verifier beyond the `jti` + receipt record.
- **Privacy:** pseudonymous principal identifiers by default (§2); accountability is resolvable through the issuing platform, not broadcast.

## 11. Open questions (inherited from the paper, §9, plus API-specific)

- **Key discovery & governance:** how do verifiers discover and trust agent-platform keys at web scale without a single gatekeeper? Who operates any registry?
- **Who governs this vocabulary** — and the standard itself (IETF/W3C-style open process is the paper's ask)?
- **Principal signing UX:** how does a non-technical owner sign a Letter legibly and safely, on a phone, without training themselves to click through?
- **Revocation propagation:** how quickly must a revocation take effect at verifiers to be meaningful?
- **Liability:** when an agent acts *within* its Letter but against the principal's true wishes, who bears it? A Letter proves delegation, not wisdom.
- **Grant transport:** header vs body for presenting the grant on subsequent API calls — left unspecified in 0.1 on purpose; real implementations should pick one and say so in their discovery document.

---

*loa/0.1 EXPERIMENTAL. Comments and breakage reports welcome — that is what a first try is for.*
