Skip to content

The API catalogue

GET
/.well-known/api-catalog

Every addressable resource of this API as one linkset, at the well-known address RFC 9727 registers for it — so a consumer that knows only the hostname can find the rest without reading anything written for a person.

application/linkset+json and nothing else. RFC 9727 § 4.2 requires that format, and this resource carries an ETag — so publishing the same bytes under application/json as well would put two negotiated representations under one validator, and a client that stored one and revalidated asking for the other would be told to reuse the wrong label. Send Accept: application/linkset+json, or no Accept at all; application/json alone is refused.

Two resources are missing from it, and deliberately. A linkset entry has to be a URI, and a template with a hole in it is not one — so the page and problem resources are described here, in this document, rather than linked there. The catalogue points at this document for that reason.

This resource answers HEAD as well, with the same headers and no body.

It also answers OPTIONS, which returns Allow, a Link to this description, and — where the resource takes one — the query formats it reads, as Accept-Query.

Response Body

application/linkset+json

application/problem+json

curl -X GET "https://example.com/.well-known/api-catalog"
Empty
---
title: "The API catalogue"
description: "Every addressable resource of this API as one linkset, at the well-known address RFC 9727 registers for it — so a consumer that knows only the hostname can find the rest without reading anything written for a person.\n\n**`application/linkset+json` and nothing else.** RFC 9727 § 4.2 requires that format, and this resource carries an `ETag` — so publishing the same bytes under `application/json` as well would put two negotiated representations under one validator, and a client that stored one and revalidated asking for the other would be told to reuse the wrong label. Send `Accept: application/linkset+json`, or no `Accept` at all; `application/json` alone is refused.\n\n**Two resources are missing from it, and deliberately.** A linkset entry has to be a URI, and a template with a hole in it is not one — so the page and problem resources are described here, in this document, rather than linked there. The catalogue points at this document for that reason.\n\nThis resource answers `HEAD` as well, with the same headers and no body.\n\nIt also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`."
url: "https://lateano.com/developers/api/reference/discovery/catalog.get"
author: "Colin Lateano"
---

# The API catalogue

Every addressable resource of this API as one linkset, at the well-known address RFC 9727 registers for it — so a consumer that knows only the hostname can find the rest without reading anything written for a person.

**`application/linkset+json` and nothing else.** RFC 9727 § 4.2 requires that format, and this resource carries an `ETag` — so publishing the same bytes under `application/json` as well would put two negotiated representations under one validator, and a client that stored one and revalidated asking for the other would be told to reuse the wrong label. Send `Accept: application/linkset+json`, or no `Accept` at all; `application/json` alone is refused.

**Two resources are missing from it, and deliberately.** A linkset entry has to be a URI, and a template with a hole in it is not one — so the page and problem resources are described here, in this document, rather than linked there. The catalogue points at this document for that reason.

This resource answers `HEAD` as well, with the same headers and no body.

It also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`.

```json
{
  "openapi": "3.1.1",
  "info": {
    "title": "lateano.com",
    "version": "1.0.0"
  },
  "paths": {
    "/.well-known/api-catalog": {
      "get": {
        "operationId": "catalog.get",
        "summary": "The API catalogue",
        "tags": [
          "Discovery"
        ],
        "description": "Every addressable resource of this API as one linkset, at the well-known address RFC 9727 registers for it — so a consumer that knows only the hostname can find the rest without reading anything written for a person.\n\n**`application/linkset+json` and nothing else.** RFC 9727 § 4.2 requires that format, and this resource carries an `ETag` — so publishing the same bytes under `application/json` as well would put two negotiated representations under one validator, and a client that stored one and revalidated asking for the other would be told to reuse the wrong label. Send `Accept: application/linkset+json`, or no `Accept` at all; `application/json` alone is refused.\n\n**Two resources are missing from it, and deliberately.** A linkset entry has to be a URI, and a template with a hole in it is not one — so the page and problem resources are described here, in this document, rather than linked there. The catalogue points at this document for that reason.\n\nThis resource answers `HEAD` as well, with the same headers and no body.\n\nIt also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The API catalogue",
            "content": {
              "application/linkset+json": {}
            }
          },
          "304": {
            "description": "The representation this request already holds is still current, so it is not sent again. No body. Read the validator off `ETag` and send it back as `If-None-Match`.",
            "headers": {
              "etag": {
                "schema": {
                  "type": "string"
                }
              },
              "cache-control": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "default": {
            "description": "Every other status this operation answers with is a refusal, and carries `problem+json` (RFC 9457).\n\nEvery refusal this API can make, whatever the status. Match on the `type` URI’s last segment — the slug below — rather than on the whole URI, which follows whichever deployment answered.\n\n| Status | Slug | Title |\n| --- | --- | --- |\n| 400 | [`invalid-idempotency-key`](https://lateano.com/api/problems/invalid-idempotency-key) | The Idempotency-Key header carries a value this resource will not accept |\n| 400 | [`malformed-body`](https://lateano.com/api/problems/malformed-body) | The request body did not parse |\n| 400 | [`missing-idempotency-key`](https://lateano.com/api/problems/missing-idempotency-key) | This resource requires an Idempotency-Key header |\n| 400 | [`missing-media-type`](https://lateano.com/api/problems/missing-media-type) | The request carried a body with no media type |\n| 404 | [`no-such-resource`](https://lateano.com/api/problems/no-such-resource) | There is nothing at this address |\n| 405 | [`method-not-allowed`](https://lateano.com/api/problems/method-not-allowed) | This resource does not answer that method |\n| 406 | [`not-acceptable`](https://lateano.com/api/problems/not-acceptable) | This resource cannot answer in any media type the request accepts |\n| 409 | [`conflicting-idempotency-key`](https://lateano.com/api/problems/conflicting-idempotency-key) | That Idempotency-Key already names a different submission |\n| 412 | [`precondition-failed`](https://lateano.com/api/problems/precondition-failed) | The condition the request carried does not hold |\n| 415 | [`unsupported-media-type`](https://lateano.com/api/problems/unsupported-media-type) | The request body is in a media type this resource does not read |\n| 422 | [`invalid-query`](https://lateano.com/api/problems/invalid-query) | The query parsed but does not describe a question this resource can answer |\n| 422 | [`invalid-submission`](https://lateano.com/api/problems/invalid-submission) | The submission parsed but is not something this resource can record |\n| 429 | [`too-many-requests`](https://lateano.com/api/problems/too-many-requests) | The courtesy quota for this caller is spent |\n| 500 | [`unexpected-failure`](https://lateano.com/api/problems/unexpected-failure) | The resource failed while answering |",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDocument"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ProblemDocument": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "§ 3.1.1. Absolute, on the origin that answered, and it resolves."
          },
          "title": {
            "type": "string",
            "description": "§ 3.1.3. The catalogue row's, unchanged from occurrence to occurrence."
          },
          "status": {
            "type": "number",
            "description": "§ 3.1.2. The same number as the status line, derived rather than passed."
          },
          "instance": {
            "type": "string",
            "description": "§ 3.1.5 RECOMMENDS an absolute URI here. This field carries a full path from the root, which is the fallback form the same section permits."
          },
          "detail": {
            "type": "string",
            "description": "§ 3.1.4. This occurrence only, bounded by `DETAIL_MAX`, absent when there is nothing to add."
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "instance"
        ],
        "additionalProperties": false,
        "description": "The schema generator publishes this declaration as the error schema, so a field here reaches the reference. RFC 9457 § 3.1 makes all five members optional, and this API narrows four of them to required: § 3.1.1 reads an absent `type` as `about:blank` and discards the resolvable URI. An optional member is absent, never present and `undefined`, because `JSON.stringify` drops the value and the wire cannot tell the two apart."
      }
    }
  }
}
```