# 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](/docs/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, a body sent as another media type, or a failure code changed. An acceptance is a file in
`acceptances/` saying a person accepts the change
([Acceptances](/docs/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.

`jadpo check` compares each API's record with the record on the default
branch at its merge base, and names each break with the entry an
acceptance lists: `"main GET /todos/{todo_id: Uuid}":
ApiChange.route_removed`, `main.CreateTodo.due:
ApiChange.input_field_required`, `main.CreateTodo.title:
ApiChange.rule_tightened` (a bound raised or lowered, a rule added, a type
changed, `none` no longer allowed, an enum variant gone),
`main.TodoView.priority: ApiChange.output_field_removed` or
`.output_field_retyped`, and `main.todo_locked: ApiChange.failure_changed`
(the code gone or its status changed). A route that now takes or
answers with another type is compared field by field with the one it had. A
route that now sends another media type, or starts or stops sending raw bytes,
is `"main GET /deeds/{deed_ref: Text}": ApiChange.media_type_changed`
([Sending raw bytes](/docs/output-and-status.md#sending-raw-bytes)).
A route made open to anyone is `PolicyChange.route_public`
([Protecting policy](/docs/protecting-policy.md)).

`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. `jadpo new` writes one listing them.
