# Discover and resolve a catalogue

Load one published catalogue project and use its definitions with an explicit
selection profile. This document is vocabulary and resolution data; consuming
it does not produce an authorization grant.

## Request contract

GET `https://api.capability.service.m7.org/v1/capabilities.json` accepts only
these query fields:

| Field | Required | Type and behavior |
| --- | --- | --- |
| `project_id` | Yes | Non-nil dashed UUID string; case-normalized. The project must be active and have public discovery enabled. |
| `revision` | No | Positive decimal integer string, 1–16 digits, no leading zero. Omit for current; supply an existing immutable catalogue revision to pin a snapshot. |

```bash
curl --fail-with-body --silent --show-error \
  --get 'https://api.capability.service.m7.org/v1/capabilities.json' \
  --data-urlencode 'project_id=PROJECT_UUID' \
  --data-urlencode 'revision=1'
```

No bearer credential, JSON request body or organization header is needed.
An explicit revision must exist; a current published project with no saved
catalogue can return version zero with empty definitions.

## Catalogue fields

| Field | Meaning |
| --- | --- |
| `catalogue_version` | Integer snapshot revision, scoped to this project. |
| `families` | List of family objects with an `id` and optional descriptive metadata. |
| `capabilities` | List of primitive objects: `id`, `family`, `operation` (`read` or `write`), optional metadata. |
| `presets` | List of preset objects: `id`, exact primitive `capabilities` list, positive integer `expansion_version`, optional metadata. |
| `role_defaults.mappings` | Object mapping role identifiers to `{id, expansion_version}` for a preset, or null. |
| Additional top-level fields | Catalogue metadata, such as a schema version or description. These are not credentials or principal facts. |

Identifiers use lowercase dotted segments, each starting with a letter and
continuing with letters, digits or underscores. Use the document's actual
IDs, including its explicit expansions. Example vocabulary only:

```json
{
  "schema_version": 1,
  "catalogue_version": 1,
  "families": [{"id": "record"}],
  "capabilities": [
    {"id": "record.fetch", "family": "record", "operation": "read"},
    {"id": "record.store", "family": "record", "operation": "write"}
  ],
  "presets": [
    {"id": "preset.reader", "capabilities": ["record.fetch"], "expansion_version": 1}
  ],
  "role_defaults": {
    "mappings": {
      "member": {"id": "preset.reader", "expansion_version": 1},
      "guest": null
    }
  }
}
```

Store both the project ID and returned catalogue revision when you need to
reproduce a decision. Preset expansion revisions change when their primitive
selection changes. A role mapping carries the selected expansion revision.
Historical revisions remain immutable; a later edit produces another current
snapshot. Public access to historical revisions still depends on the project's
current active/public state.

## Resolution semantics

Use these rules when loading the document into an M7 capability consumer:

1. Resolve primitive IDs to themselves and preset IDs to their declared exact
   primitive lists. Family selections represent the family's primitives;
   `<family>.read` and `<family>.write` select its operation class, and
   `readonly` selects read actions. Published exact expansions take precedence.
2. A nonempty explicit profile replaces role fallback, including when every
   supplied ID is unknown. Unknown selections yield no primitives.
3. Only an empty profile can use the fallback role's mapping. A null role
   mapping grants nothing. If there is no mapping, a known role-named pack
   can be selected by the consumer.
4. Union the selected primitive expansions. Resolution/checking is
   case-insensitive. A pack check succeeds only when all its primitives are
   selected; unknown IDs and empty packs deny.

`readonly` is a selection, not a deny override: adding `record.store` to it
also selects that write. A fallback role or selected Owner-named preset never
makes a principal an actual project Owner. The consuming service applies its
current caller, membership, role, resource and lifecycle checks independently.

Fetch, parse and validate the complete document before replacing a previously
loaded catalogue. Do not combine definitions from different projects or
revisions. Do not treat a failed discovery request as a successful empty
catalogue or invent fallback grants.

## Errors

| HTTP status | Failure |
| --- | --- |
| `400` | Unknown query field; missing/invalid/nil `project_id`; malformed `revision`. |
| `404` | Project missing, private or archived; explicit revision not found. |
| `503` | Storage unavailable or stored catalogue invalid. |

Errors include `status: 0` and `data.code`; no successful empty definition
document is substituted for an unavailable catalogue. See the
[publication and authorization boundaries](https://m7.org/docs/api/api.capability.service.m7.org/authorization.md).
