Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ jobs:
- run: pip install pyyaml
- name: Check the json, yaml and bash blocks in the Markdown files
run: python3 .github/scripts/check_examples.py
- name: Run the Harbor Library examples against the sandbox
run: python3 api-reference/harbor-library/tests/run_examples.py
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ Five samples, ranked by what they prove:

### API reference

Published help center documentation. I wrote these.
**New: a complete API reference I designed and directed, with examples that run.** Then the published help center documentation I wrote.

| Sample | What it shows | My role |
|---|---|---|
| [Harbor Library Lending API](api-reference/harbor-library/README.md) | A full reference for a made-up API: quickstart, authentication, idempotency, pagination, errors, rate limits, OpenAPI file. A sandbox runs every example, and a GitHub Action fails if one stops matching. | Designed and directed. Drafted with Claude, reviewed by four reader personas, edited by me |

**Published snapshots** of help center pages I wrote. They are copies of the published articles. Their examples ran in the vendor's online API reference, so they are not runnable here. The Harbor Library sample above is the one you can run.

| Sample | What it shows |
|---|---|
Expand Down
2 changes: 2 additions & 0 deletions api-reference/get-company-members.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ original_format: PDF (published help center article)

# Get Company Members

> **Snapshot.** This is a copy of a published help center article, kept as I wrote it. Its examples ran in the vendor's online API reference, where the tabs mentioned below showed code in each language. Those tabs are not part of this copy, and the endpoint has since changed. For a reference whose examples run, see the [Harbor Library Lending API](harbor-library/README.md).

How to use the company members endpoint to retrieve member information.

The company members endpoint retrieves the name, email address, and other details for a member of your company using the parameters you choose.
Expand Down
31 changes: 31 additions & 0 deletions api-reference/harbor-library/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Harbor Library Lending API: a documentation sample

A complete, small API reference for a library lending service: quickstart, authentication, pagination, reference pages, errors, rate limits, and a changelog. The API is made up. **Every example in these pages runs.**

**Written for** developers who build against an API and want to see how I document one. **To show** the parts of API documentation that decide whether a developer succeeds on the first try: a working first call, an errors page that tells you what to do next, and examples that cannot drift from the truth.

## What to open first

1. [Quickstart](docs/quickstart.md): a first loan in about five minutes.
2. [Errors](docs/errors.md): every error code, its cause, and its fix.
3. [Loans reference](docs/reference/loans.md): the endpoint with the most going on.
4. [OpenAPI file](openapi.yaml): the source of truth the pages follow.

The rest: [Authentication](docs/authentication.md), [Idempotency](docs/idempotency.md), [Pagination](docs/pagination.md), [Books](docs/reference/books.md), [Members](docs/reference/members.md), [Rate limits](docs/rate-limits.md), [Changelog](docs/changelog.md), and [Easter eggs](docs/easter-eggs.md), which are real and listed so nobody mistakes them for bugs.

## Why the examples cannot go stale

`mock/server.py` is a sandbox with no dependencies. `tests/run_examples.py` finds every `curl` example in the pages, runs it against the sandbox, and fails if the answer differs from the response shown. It also fails if a status is not one the OpenAPI file lists for that route, or if an error code the sandbox can return is missing from the errors page. A GitHub Action runs it on every change.

Each page assumes a fresh sandbox, and the check restarts the sandbox for every page. Run it yourself:

```bash
pip install pyyaml
python3 api-reference/harbor-library/tests/run_examples.py
```

## How this was made

I designed the API and directed the work. An AI tool (Claude) drafted the pages and the sandbox. Four AI reader personas then reviewed the pages over several rounds, and I edited and approved the result. The sandbox and the checks are what keep the AI-drafted parts honest.

The library, its members, and its books are invented. Real authors and titles appear only as sample data.
69 changes: 69 additions & 0 deletions api-reference/harbor-library/docs/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Authentication

**Written for** a developer who is about to make the first real call, **to** send the API key correctly and understand the two ways it can fail.

Every endpoint except the [teapot](easter-eggs.md) needs an API key. Send it in the `Authorization` header with the word `Bearer` in front. `Bearer` means "whoever carries this key is allowed in."

```bash
curl "http://127.0.0.1:8080/v1/members/mem_ada" \
-H "Authorization: Bearer hl_test_sandbox"
```

```json
{
"id": "mem_ada",
"name": "Ada Lovelace",
"card_status": "active",
"fines_cents": 0,
"open_loans": 1
}
```

The sandbox accepts one key, `hl_test_sandbox`. It is public on purpose and only works against the sandbox.

Keep a real key out of source code, URLs, and logs. Anyone who holds it can lend and return books as your library.

## When the key is missing or wrong

A request with no `Authorization` header, or one that does not start with `Bearer`, gets `401` and the code [`unauthorized`](errors.md#unauthorized) (the header is missing, or it does not start with `Bearer`):

```bash
curl "http://127.0.0.1:8080/v1/members/mem_ada"
```

```json
{
"error": {
"status": 401,
"code": "unauthorized",
"message": "Send your API key as: Authorization: Bearer YOUR_KEY.",
"doc_url": "https://docs.harborlibrary.example/errors#unauthorized",
"request_id": "req_000002"
}
}
```

A request with a key the API does not recognize also gets `401`, with the code [`invalid_api_key`](errors.md#invalid_api_key):

```bash
curl "http://127.0.0.1:8080/v1/members/mem_ada" \
-H "Authorization: Bearer hl_test_wrong"
```

```json
{
"error": {
"status": 401,
"code": "invalid_api_key",
"message": "That API key is not recognized.",
"doc_url": "https://docs.harborlibrary.example/errors#invalid_api_key",
"request_id": "req_000003"
}
}
```

Fix: check that the header starts with `Bearer `, and that the key is copied whole, with no spaces or line breaks. Do not retry a `401` unchanged.

## Next

[Pagination](pagination.md), or the [quickstart](quickstart.md) if you have not run it yet.
15 changes: 15 additions & 0 deletions api-reference/harbor-library/docs/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

**Written for** a developer who needs to know whether an update can break their code, **to** read what changed and when.

Changes to the Harbor Library Lending API, newest first.

## How versions work

The `v1` at the start of every path is the **major version**. A change that could break existing code, such as removing a field or changing what a status code means, gets a new major version at a new path, such as `/v2`. Adding a field or an endpoint does not. Your code should ignore fields it does not know.

When a new major version ships, the old one keeps working for at least 12 months. The release is announced here first. The version number in this file follows [semantic versioning](https://semver.org).

## 1.0.0 (2026-10-26)

First release. Books, members, and loans. Cursor pagination. Idempotent loan creation. Error codes and rate limits as documented here.
60 changes: 60 additions & 0 deletions api-reference/harbor-library/docs/easter-eggs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Easter eggs

**Written for** anyone who found a strange response in the sandbox, **to** confirm it is a joke on purpose and not a bug.

The sandbox has a few jokes. They are real, they are stable, and they are listed here. None of them can happen by accident.

## Ask for coffee

`GET /v1/teapot` needs no API key and always returns `418`. Other methods on that path return `405`, with or without a key. The status comes from [RFC 2324](https://www.rfc-editor.org/rfc/rfc2324), the Hyper Text Coffee Pot Control Protocol, an April Fools' joke from 1998 that web servers have kept ever since.

```bash
curl "http://127.0.0.1:8080/v1/teapot"
```

```json
{
"error": {
"status": 418,
"code": "teapot",
"message": "I'm a teapot, short and stout. The library does not brew coffee.",
"doc_url": "https://docs.harborlibrary.example/errors#teapot",
"request_id": "req_000001"
}
}
```

## The answer

Book `bk_042` is *The Hitchhiker's Guide to the Galaxy*. The library owns 42 copies:

```bash
curl "http://127.0.0.1:8080/v1/books/bk_042" \
-H "Authorization: Bearer hl_test_sandbox"
```

```json
{
"id": "bk_042",
"title": "The Hitchhiker's Guide to the Galaxy",
"author": "Douglas Adams",
"copies_total": 42,
"copies_available": 42
}
```

## Quiet, please

The rate limit error is called [`shush`](errors.md#shush), and its message asks you to be quiet. See [rate limits](rate-limits.md).

## Rip Van Winkle

Member `mem_rip` borrowed *Frankenstein* on 2006-03-14 and has not returned it. His fines are $1,240.00, so his card is blocked. See [members](reference/members.md) and the [`member_blocked`](errors.md#member_blocked) error.

## The librarian

Every response carries the header `X-Librarian: Marian`. Check with `curl -i`. It is a nod to the librarian in *The Music Man*.

## Next

[Back to the quickstart](quickstart.md).
Loading
Loading