# The public API record

Each API has a committed, readable record of its routes, fields and
failure codes: `api/main.jadpo_record` for `main`. `jadpo check` fails
while the record disagrees with the source, and `jadpo api update`
rewrites it. So every change to the API is a diff of one file, and a
breaking change is caught at review, not by a client in production.

The record is written in the
[record format](what-is-committed-and-why.md). Its
first line says what generated it and which command updates it. A hand
edit is `record.edited`, and a record out of date is `record.out_of_date`.

Here is a whole route group, with what it runs, and the record
`jadpo api update` writes for it:

```jadpo
use planning/entities { Todo }
use planning/types { TodoView }
use planning/actions { CreateTodo, create_todo }
use planning/queries { find_todo }

/// Todos: making one and reading one back.
routes Todos {
    prefix: /todos

    /// Creates a todo.
    POST / {
        authentication: RouteAuthentication.none
        input: CreateTodo
        output: TodoView
        status: SuccessStatus.created
        run: create_todo(input)
    }

    /// One todo.
    GET /{todo_id: Todo.id} {
        authentication: RouteAuthentication.none
        output: TodoView
        run: find_todo(todo_id)
    }
}
```

```jadpo
// Generated by Jadpo from the routes of API main; update it with `jadpo api update`, never by hand.
record api 1

GET /openapi.json
    generated openapi
    authentication none
    status 200

POST /todos
    authentication none
    input CreateTodo (body)
    output TodoView
    status 201

GET /todos/{todo_id: Uuid}
    authentication none
    output TodoView
    status 200
    fails todo_not_found

input CreateTodo
    title: Text { min_length: 1, max_length: 200 }

output TodoView
    done: Bool
    id: Uuid
    title: Text { min_length: 1, max_length: 200 }

failure todo_not_found
    status 404
    message "Todo not found."
    todo_id: Uuid
```

## What the record holds

- **The wire contract.** A field shows what a client sends or reads.
  Objects and enums have an entry of their own, and each failure code has
  an entry with its status, message and client fields.
- **How clients sign in.** An API that signs clients in says how in an
  `api main` entry, one line per method (`authentication
  browser_session`). A route for one kind of caller says which
  (`caller user`). So switching an API from bearer tokens to cookies is a
  diff of the record.
- **A fixed order.** Everything is sorted: routes by path, then method;
  types, fields and variants by name; failures by code. Reordering the
  source changes nothing.
- **No version.** Two branches that both changed the API would always
  conflict on one. The OpenAPI document's version is the first 12 hex
  digits of the record's SHA-256, computed at build and never stored.

## Breaking changes

A change that can break a client also needs an acceptance written by a
person: a route removed, a required input field added, an output field
removed, or a failure code changed. An acceptance is a file in
`acceptances/` saying a person accepts the change
([Acceptances](acceptances.md)). Every change that
needs one uses the same form, such as a database drop, a policy weakening
or a new domain: one file per decision, listing the changes it allows,
counted only in the change that adds it.

`acceptances/` and `api/` are owned in CODEOWNERS, the file a code host
reads to know who must approve changes to a path, so the host's review
shows who approved it.

> Telling breaking changes apart and requiring their acceptance aren't
> built yet ([What is built](records-and-manifest-built.md)).
> Until they are, the record's
> diff is what a reviewer reads.
