# auth.md — Oxford Ledge agent registration and sign-in

Oxford Ledge is a financial-data and financial-education service for lifelong
students and investors: SEC-filing derived company fundamentals, insider
transactions, 13F institutional ownership, a BDC private-credit corpus with
loan-level holdings, macro series, a ~1,400-term glossary, and a structured
LEARN curriculum.

This document tells an AI agent acting on behalf of a human user how to
register, obtain a credential, and call the API. It is written for machine
reading; every endpoint below is live at `https://www.oxfordledge.com`.

## What an agent gets from a registered account

Anonymous access works for public pages and is rate-limited per IP. A
registered account is what makes an agent useful to its user:

- **A stable API credential.** Header authentication (`X-API-Key`) instead of
  scraping HTML, with a documented JSON contract per endpoint
  (`/openapi.yaml`, human docs at `/api-docs`).
- **Materially higher limits.** A standard key allows 60 requests/minute and
  10,000 calls/month; a premium key allows 300/minute and 100,000/month.
  Anonymous IP-based limits are far lower and are not guaranteed.
- **Per-user state the agent can read and write on the user's behalf** —
  watchlists, portfolios, paper-trading positions, saved screens, and LEARN
  progress. None of this is reachable without an account.
- **Tier-gated datasets.** Some endpoints require a paid tier and return HTTP
  402 with a structured payload naming the required tier when they are not
  available to the current account. See "Tiers and 402s" below.
- **Attributable usage.** Calls are attributed to the account rather than to a
  shared IP bucket, so one agent's traffic cannot exhaust another's allowance.

## Registration (how)

Oxford Ledge does not currently publish OAuth Protected Resource Metadata or
Authorization Server metadata; there is no dynamic client registration
endpoint. Registration is account-based, and this document is the complete
description of it. Agents should not probe for `/.well-known/oauth-*`
documents — their absence is intentional, not an error.

**Method 1 — user-delegated account registration (the supported path).**

1. `GET /api/auth/challenge` returns a proof-of-work challenge.
2. Compute `solution` such that `SHA256(challenge + solution)` begins with
   `000`.
3. `POST /api/auth/register` with JSON `{email, password, challenge,
   solution}` plus optional `displayName`, `experience`, `useCase`,
   `referralSource`, `timezone`. The endpoint is CSRF-protected and
   rate-limited per client IP.
4. The address receives a verification email; the account is confirmed via
   `GET /api/auth/verify-email`. Email verification is required before an
   agent-usable credential can be issued.
5. `POST /api/auth/login` establishes an authenticated session
   (`HttpOnly` cookie). `GET /api/auth/me` returns the current account and
   tier.

The human user supplies the email address, the password, and the consent to
create an account. An agent must not invent credentials or register accounts
that no user asked for.

**Method 2 — API key issuance (after registration).**

- `POST /api/keys` (authenticated, CSRF-protected) issues a key. The raw key
  is returned **once**; only its prefix is retrievable afterwards. Store it as
  a secret.
- `POST /api/keys/trial` issues a one-per-lifetime 14-day developer trial key
  capped at 100 calls/day, for evaluation before subscribing. Requires a
  verified email.
- `GET /api/keys/list` lists key prefixes and metadata; `DELETE /api/keys/{id}`
  revokes.

## Credential use

Send the key in a request header on every API call:

```
X-API-Key: <your key>
```

Keys are hashed at rest and never returned in full after issuance. A revoked
or expired key returns HTTP 401. Exceeding the per-minute or monthly allowance
returns HTTP 429 — back off rather than retrying immediately.

## Tiers and 402s

Endpoints that require a paid tier return HTTP **402 Payment Required** with a
JSON body naming `requiredTier`, `currentTier`, and an `upgrade_url`. An agent
should surface that to its user rather than retrying; 402 is a statement about
the account, not a transient failure. HTTP 401 means "not authenticated", and
403 is reserved for CSRF and admin surfaces.

## Machine-readable neighbours

- `/openapi.yaml` (also `/.well-known/openapi.yaml`) — the full OpenAPI
  description of the public API.
- `/llms.txt` — site context and crawl-friendly paths for AI clients.
- `/datasets` — the public data catalog (schema.org `DataCatalog`).
- `/api-docs` — human-readable API documentation.
- `/robots.txt` — crawl policy.

## Contact

Registration or access problems: hemal.shah902@gmail.com. Data is sourced from
SEC EDGAR, FRED, FDIC and other public datasets; it is educational and is not
investment advice.
