# Pin the contract your app is built on

> Find the REST contract your Anectico deployment serves, verify its digest, and fetch the operations your program calls.

Canonical page: https://anectico.com/docs/apps/contract/


A program you build on Anectico's REST API must be written against the contract of the deployment it
calls, not against whatever the documentation site describes today. The documentation can be newer
than the deployment, and a URL that contains `/api/v1` says nothing about which operations, fields and
limits that deployment actually serves. Every deployment therefore publishes the exact contract it
serves, with a digest, and your agent can fetch and verify it before it writes a line of code.

Until Anectico's first supported release the contract is experimental: routes and response shapes can
change without notice, so record the revision and the digest you built against and re-test when either
changes (see [Compatibility](#compatibility)).

## Find the contract a deployment serves

`GET /api/v1/api-contract` answers with the deployment's contract manifest. It needs no credential, so
your agent can read it before any key exists, and it holds no organization, project, person or credential
data: every caller of one deployment receives the same document. It accepts no query parameters; one is
refused with `400` rather than ignored. It never fetches the documentation site's manifest, so it can only
describe the build that answers.

```bash
curl --fail-with-body https://app.anectico.com/api/v1/api-contract
```

```json
{
  "schema_version": 1,
  "serving_build": "2026.10.15",
  "contract_revision": "experimental-03f7a62d28df",
  "digest": "sha256:ab7b71b7…",
  "audience_counts": { "app_data": 432, "customer_control": 164, "operator": 25, "protocol": 35, "sdk_data_plane": 18 },
  "app_qualification_counts": { "supported": 3, "restricted": 277, "experimental": 152, "excluded": 0 },
  "active_operation_ids": ["get_analytics_insights_by_id", "…"],
  "active_audience_counts": { "app_data": 432, "…": 0 },
  "protocol_availability": [{ "operation_id": "mcp", "route": "/mcp", "active": true }],
  "artifacts": [{ "partition": "customer", "revision": "experimental-03f7a62d28df", "digest": "sha256:4f9c426f…", "url": "https://anectico.com/docs/api-contract/artifacts/…", "media_type": "application/json", "byte_count": 10635930, "operation_count": 614, "protocol_entry_count": 0, "qualification_boundary": "…" }],
  "proof_links": { "viewing": ["…"], "none": ["…"] },
  "stability": { "status": "experimental", "pin": "…", "policy_reference": "https://anectico.com/docs/apps/contract/" }
}
```

| Field | Meaning |
| --- | --- |
| `serving_build` | The release or build identity of the deployment that answered. |
| `contract_revision` | The exact revision of the contract this deployment serves. One revision is one set of bytes: it is derived from the manifest's own content. |
| `digest` | The SHA-256 of the master contract, written `sha256:` and 64 lowercase hex characters. The master contract is the complete census, which includes operations that are not part of any customer contract, so it is not downloadable. It is a different hash from the digest of any artifact below. |
| `audience_counts`, `app_qualification_counts` | How many operations the revision declares, by audience and by app qualification. These are what the revision contains, not what this deployment mounted. |
| `active_operation_ids`, `active_audience_counts`, `protocol_availability` | What this deployment mounted when it started. An operation that the revision declares but the deployment did not mount is not served here, and is not listed. A configuration change can change these without changing any digest. |
| `artifacts` | The contracts you can download: `customer` (every app-data, customer-control and SDK operation, each labeled with its audience and qualification), `apps` (the app-data operations that are supported, or restricted with the reason stated) and `protocol` (sign-in, discovery, MCP and receiver flows). Each has its own digest, size, immutable address and a statement of what it does and does not promise. |
| `proof_links` | Which console proof pages exist and which query parameters each needs, and the resource types that have no proof page. It carries no link and no origin; a link comes only from a result that has a page. |
| `stability` | `experimental` until the first supported release. |

An operation's stable ID is its `operationId`; `active_operation_ids` lists the ones you can select.

## Write the contract to a file

`anectico api spec` fetches the manifest of the configured deployment, downloads the exact artifact it
names, verifies the artifact, and writes it where you say. It sends no API key, token or cookie to either
host: the contract is public, and the documentation host is not the API.

```bash
# The whole customer contract
anectico api spec --out ./anectico-api.json

# Only the operations your program calls, with the schemas they reference
anectico api spec \
  --operation get_oncall_schedules_by_id_on_call \
  --operation get_analytics_results_by_result_id \
  --revision experimental-03f7a62d28df \
  --out ./anectico-api.json
```

| Flag | Meaning |
| --- | --- |
| `--out` | The file to write. Required. An existing file is replaced only with `--force`. It is not the global `--output`, which chooses how the result is printed. |
| `--operation` | A stable operation ID; repeat it to select several. Without it the whole contract is written. |
| `--revision` | Refuse unless the deployment serves exactly this revision. A different revision is an error, never a fallback to the newest published one. |
| `--digest` | Refuse unless the digest of the source artifact is this. Accepts `sha256:` followed by 64 hex characters, or the 64 hex characters `sha256sum` prints. The master `digest` is refused. |
| `--partition` | The source contract: `customer` (default), `apps` or `protocol`. |
| `--force` | Replace an existing `--out` file. |

Before anything is written the command checks the manifest format, the revision pin, the artifact's size
and its SHA-256 against what the manifest advertises, that the artifact is the contract it claims to be,
and that every reference inside it resolves. If any check fails, nothing is written: there is never a
half-written spec that looks plausible. An operation your deployment did not mount is refused.

### Three digests that are not interchangeable

| Digest | What it hashes | Where you meet it |
| --- | --- | --- |
| Master | The complete census | The manifest's `digest` |
| Source artifact | The exact bytes of one downloadable contract | `artifacts[].digest`, `--digest`, and `source_digest` inside a selected file |
| Selected output | The exact bytes of the file `--operation` writes | The `selected_output_digest` the command prints |

`--digest` compares the source artifact's digest, so it still holds for every selection cut from that
artifact. Supplying the master digest is refused with a message that says so.

### A selected file says it is a selection

The file `--operation` writes is a valid OpenAPI 3.1 document containing only the operations you named
and the schemas, responses and security schemes they reference. It carries an `x-anectico-selection`
extension so it cannot be mistaken for the complete contract:

```json
"x-anectico-selection": {
  "selected_count": 2,
  "source_digest": "sha256:4f9c426f…",
  "source_revision": "experimental-03f7a62d28df"
}
```

`source_digest` is the digest of the verified source artifact, not of the selected file. The document never
embeds its own digest: `selected_output_digest` is the SHA-256 of exactly the bytes written, and it is the
same whether you selected through the CLI or through MCP.

## Read the contract over MCP

With `mcp:read` and `docs:read`, the `get_rest_contract` read action returns the same manifest, or the
declarations of the operations you name. It spends no tool slot of its own: call it through
`execute_read_action`.

```json
{
  "name": "execute_read_action",
  "arguments": {
    "action": "get_rest_contract",
    "arguments": {
      "operation_ids": ["get_oncall_schedules_by_id_on_call"],
      "expected_revision": "experimental-03f7a62d28df"
    }
  }
}
```

- With no `operation_ids` it returns the manifest and nothing larger.
- With 1 to 100 unique `operation_ids` it returns `selected_count`, `complete: true`, the source revision
  and digest, the `selected_output_digest` and the `document`, the exact canonical bytes the CLI would
  write. Write the document verbatim to keep it verifiable.
- `expected_revision` refuses an answer from any other revision.
- A selection whose document would exceed one MiB is refused with `selection_too_large`; nothing is cut to
  fit, `complete` is `false` and `selected_count` is `0`. Select fewer operations, or use the CLI for the
  whole file.

| Refusal | Meaning |
| --- | --- |
| `invalid_selection` | Zero or more than 100 operation IDs, a repeated or oversized ID, or an unknown partition. |
| `unknown_operation` | The contract of this revision has no such operation. |
| `operation_not_active` | The revision declares the operation but this deployment did not mount it. |
| `revision_mismatch` | The deployment serves another revision than `expected_revision`. |
| `selection_too_large` | The selected document would exceed one MiB. |

An ordinary runtime key that only calls the API needs none of this: `docs:read` is a discovery grant for
the agent that builds your program, not a permission your program holds.

## The published copies

The same revision is published, immutably, on the documentation site:

| Address | What it is |
| --- | --- |
| `https://anectico.com/docs/api-contract/latest.json` | The manifest of the newest published revision. It selects a revision; it never replaces one. |
| `https://anectico.com/docs/api-contract/revisions/<revision>.json` | The manifest of one revision. It never changes. |
| `https://anectico.com/docs/api-contract/artifacts/anectico-<partition>-<sha256>.openapi.json` | One contract. The file name contains the SHA-256 of its bytes, so a name can never refer to two different files. |

Treat `latest.json` as news about what exists, not as the contract of your deployment: the deployment's own
`GET /api/v1/api-contract` is. To check a downloaded file yourself:

```bash
sha256sum anectico-customer-4f9c426f….openapi.json
```

The result must equal the 64 characters in the file name and the `digest` the manifest lists for it.

## Compatibility

Until the first supported release there is no compatibility promise:

- routes and response shapes may change without a deprecation period;
- every contract is experimental, including the operations marked supported;
- clients pin the serving build and the digest they tested, and re-test before moving.

At the first supported release, the release notes name the exact set of supported app operations and their
compatibility and deprecation policy, and the manifest's `stability` records the release the promise starts
in. The promise starts there and is never backdated; an operation that is merely listed in a contract, or in
the [REST reference](/docs/reference/rest-api), is not stable because it is listed.

## Related

- [Build an app on the REST API](/docs/apps/architecture)
- [REST API conventions](/docs/reference/rest-api)
- [Machine-readable documentation](/docs/reference/machine-readable-docs)
- [CLI command reference](/docs/reference/cli)

## Browser aggregate sharing

Read the **customer** contract partition for owner grant controls and dedicated mint and view protocols.
These operations are excluded from the ordinary app-data subset. Pin and inspect their actual
credential, confirmation, paging and refusal declarations; a protocol exclusion does not mean unavailable.

See [Share a saved aggregate with a browser page](/docs/apps/browser-aggregate-view) for the full
credential, theft, lifetime, privacy and external-copy contract.
