# API reference

Base URL: `https://agents.newyorkeverydaylawreference.com`. The machine-readable description is [`/openapi.json`](https://agents.newyorkeverydaylawreference.com/openapi.json) (OpenAPI 3.1), generated from the same schemas that validate requests. JSON Schemas for every object are at `/schema/{name}.json`.

Record reads are open. Checkout at `/checkout_sessions` is open (no merchant API key). An evidence packet (`POST /evidence-packets`, `GET /packet/{id}`, `GET /ask`) requires the access token from a paid checkout, sent as `Authorization: Bearer`. Send and expect JSON (`Content-Type: application/json`).

## Evidence objects

| Type | ID prefix | Canonical URL |
| --- | --- | --- |
| proposition | `prop_` | `/proposition/{id}` |
| authority | `auth_` | `/authority/{id}` |
| case | `case_` | `/case/{id}` |
| source | `src_` | `/source/{id}` |
| relationship | `rel_` | `/relationship/{id}` |
| packet | `pkt_` | `/packet/{id}` |

### `GET /{type}/{id}` and `GET /{type}/{id}/v/{n}`

The current version, or a specific editorial version. Query parameters:

- `as_of` (YYYY-MM-DD): the date for `applicability` and `trust.temporal_status`. Defaults to today.
- `view=compact`: the smaller form used inside packets. Only the full view lets you recompute `content_hash`.

Representations: JSON by default and for `Accept: application/json`; Markdown for `Accept: text/markdown` or a `.md` suffix; HTML for browsers (`Accept: text/html`). A `.json` suffix always gives JSON. Anything else gets `406`.

A request for an ID under the wrong type (for example `/authority/prop_...`) redirects (`301`) to the right one.

The full JSON is the stored record plus derived keys: `url`, `cite_as`, `trust`, `applicability`, `graph`, `provenance`, `links`. See [citation and portability](https://agents.newyorkeverydaylawreference.com/docs/citation) for how the hash is computed.

## Research

### `GET /search`

| Parameter | Notes |
| --- | --- |
| `q` | Required, 1–500 characters |
| `jurisdiction` | Default `us-ny-nyc`; parent jurisdictions (`us-ny`, `us`) are included |
| `as_of` | Default today; results that apply on that date are ranked first |
| `type` | `proposition`, `authority`, `case`, `source`, or `relationship` (default: the first three) |
| `limit` | 1–50, default 10 |
| `cursor` | `next_cursor` from the previous page; a cursor only works with the query that produced it |

The response includes `interpretation`: the topics matched, whether the question is in this dataset's domain, out-of-scope terms, and whether it reads as fact-specific. Matching is deterministic (whole-word synonyms from [`/topics`](https://agents.newyorkeverydaylawreference.com/topics)); no language model is involved.

### `POST /evidence-packets`

```json
{ "question": "Is the owner liable for a tree well?", "as_of": "2024-06-01", "jurisdiction": "us-ny-nyc", "topics": ["tree_wells"] }
```

Only `question` is required. `topics` adds topic IDs to those matched from the question. Returns `201` with `Location`. The packet is stored and can be fetched again at `/packet/{id}` (JSON, `.md`, or HTML). Identical requests against the same dataset return the same packet.

Packets never guess. A question outside the [coverage](https://agents.newyorkeverydaylawreference.com/docs/coverage) gets `INSUFFICIENT_EVIDENCE` with a `NO_COVERAGE` or `OUT_OF_SCOPE_TERMS` limitation. A question about a particular person's situation gets general evidence only, with a `FACT_SPECIFIC_QUESTION` limitation. See [status codes](https://agents.newyorkeverydaylawreference.com/docs/statuses).

### `POST /batch`

```json
{ "ids": ["prop_...", "case_..."], "as_of": "2024-06-01", "view": "compact" }
```

Up to 100 IDs. Returns `items` (in request order) and `not_found`.

### `GET /relationships`

`from`, `to`, `relation` (alias `type`: `establishes`, `supports`, `interprets`, `applies`, `limits`, `distinguishes`, `supersedes`, `conflicts_with`, `cites`), and `as_of` (only relationships that hold on that date). Unknown `from`/`to` IDs give `404`.

## Citations

### `POST /resolve`

```json
{ "citation": "https://agents.newyorkeverydaylawreference.com/proposition/prop_nyc_sidewalk_city_not_liable/v/1", "content_hash": "sha256:..." }
```

`citation` can be an ID (optionally `prop_...@v2`), a canonical URL (optionally with `/v/{n}`, `.json`, or `.md`), or a legal citation such as `10 NY3d 517`. The answer is always `200`; `status` says what happened. See [citation and portability](https://agents.newyorkeverydaylawreference.com/docs/citation).

## Discovery

- [`/llms.txt`](https://agents.newyorkeverydaylawreference.com/llms.txt): an index for language models.
- [`/.well-known/service.json`](https://agents.newyorkeverydaylawreference.com/.well-known/service.json): coverage, freshness, limits, and policies.
- [`/openapi.json`](https://agents.newyorkeverydaylawreference.com/openapi.json), [`/context.jsonld`](https://agents.newyorkeverydaylawreference.com/context.jsonld), [`/topics`](https://agents.newyorkeverydaylawreference.com/topics), `/health`.

## Conventions

- **Errors** are `application/problem+json` with a stable `code`; see [status codes](https://agents.newyorkeverydaylawreference.com/docs/statuses).
- **`X-Request-Id`** is on every response. Send your own (8–64 characters of letters, digits, `._-`) to correlate logs; it is also the ID of the stored trace.
- **`ETag`** is derived from the content hash plus the representation, date, and dataset. Send `If-None-Match` to get `304`.
- **`Cache-Control`**: records 5 minutes; pinned versions and packets 1 day; discovery documents 1 hour; search, batch, resolve, and packet creation are not cached.
- **`Vary: Accept`** on negotiated URLs. **`Link`** headers give `canonical`, `alternate`, and `describedby`.
- **Freshness**: every source records `retrieved` and every record `verification.verified_on`; `npm run verify` re-checks URLs and passages. The dataset is versioned by release date (`dataset_version`), and live counts and the list of unverified records are in `service.json`.
- **Rate limits**: `X-RateLimit-*` headers are placeholders and are not enforced during the preview.
- **Pricing**: one evidence packet costs 50 US cents. Stripe adds sales tax from the billing address, and the receipt total is 50 cents plus that tax. Each checkout covers that one packet, and the same packet can be read again with the same token. A single record page does not require payment.
- **Checkout abuse limits**: unpaid ACP sessions are capped in memory (default 200) and expire (default 30 minutes). Over the unpaid cap returns `429` `unpaid_session_limit`.
- **Rate limits**: 600 requests per client IP per 60 seconds (enforced). Responses include `X-RateLimit-*` headers; over the limit returns `429` `RATE_LIMITED` with `Retry-After`. `GET /health` is exempt.
- **Body size**: POST bodies over 64 KB get `413`.
- **CORS** is open (`*`).
