# Catalog API

The Catalog API lets scripts and CI jobs do what the Catalog console does: declare services and keep them current, read the inventory, decide which service a resource belongs to, read drift and SLA reports, and export everything for an audit. Every endpoint, with its parameters, schemas and errors, is in the [Catalog API reference](https://docs.evohub.io/catalog.md). This page shows the common tasks and the rules that apply to all of them.

Paths below are relative to `https://evohub.io`, and every one starts with `/api/v1/catalog/`. Requests need an API key, sent as described in [API overview](https://docs.evohub.io/api-overview.md#authentication).

## Scopes

| To | Scope |
| --- | --- |
| Read services, tiers and SLA reports | `catalog:component:read` |
| Declare, change and delete services, set their RACI, manage tiers | `catalog:component:write` |
| Read resources, changes, applications, images and the map | `catalog:resource:read` |
| Accept and reject matches, put a resource or an application in a service | `catalog:resource:write` |
| Read environments and sources | `catalog:source:read` |
| Manage environments, rename, pause and resume sources | `catalog:source:write` |
| Read cloud accounts | `identity:cloudaccount:read` |
| Connect and manage cloud accounts | `identity:cloudaccount:write` (connecting also needs `catalog:source:write`) |

The match queue, drift, a service's resources and the inventory export need both `catalog:component:read` and `catalog:resource:read`. A key without a scope gets **403**, never 401. See [Catalog permissions](https://docs.evohub.io/catalog-permissions.md).

## Declare a service

```bash
curl https://evohub.io/api/v1/catalog/components \
  -H "Authorization: Bearer $EVOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "payments-api",
    "display_name": "Payments API",
    "owner_team_id": "team_3b6f0e2a-1c4d-4e8f-9a7b-2d5c6e8f0a1b",
    "tier": 1,
    "runbook_url": "https://wiki.acme.example/runbooks/payments"
  }'
```

`slug` and `display_name` are required; the slug can never change. Every endpoint that takes a service accepts its ID (`cmp_…`) or its slug, so `GET /api/v1/catalog/components/payments-api` works. `PATCH` changes only the fields you send; send `null` to clear `owner_team_id`, `tier` or `sla_overrides`, and `""` to clear a text field.

A pipeline can keep services in step with a repository: `PATCH` each one, and `POST` it when the answer is 404.

## Find what nobody owns

```bash
curl "https://evohub.io/api/v1/catalog/resources?unowned=true&exclude_kind=Namespace,Node" \
  -H "Authorization: Bearer $EVOHUB_API_KEY"
```

Lists page with `cursor`: pass the previous answer's `pagination.next_cursor` until `has_more` is false. The first page of the resource and application lists also carries `pagination.total`. Other useful filters: `image=nginx` (any tag of a repository), `floating=true` (images with no fixed version), `component_id`, `environment_id` and `status`.

`GET /api/v1/catalog/drift` returns the whole drift report in one answer.

## Decide the match queue

```bash
# What is waiting
curl https://evohub.io/api/v1/catalog/matches \
  -H "Authorization: Bearer $EVOHUB_API_KEY"

# Accept one
curl -X POST https://evohub.io/api/v1/catalog/matches/$MATCH_ID/accept \
  -H "Authorization: Bearer $EVOHUB_API_KEY"

# Or put an application in a service directly
curl -X POST https://evohub.io/api/v1/catalog/applications/$APP_ID/component \
  -H "Authorization: Bearer $EVOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"component": "payments-api"}'
```

A rejected proposal is never proposed again. `{"component": null}` unbinds. See [Matching and drift](https://docs.evohub.io/catalog-matching-and-drift.md).

## Read an SLA report

```bash
curl "https://evohub.io/api/v1/catalog/components/payments-api/sla-report?month=2026-10" \
  -H "Authorization: Bearer $EVOHUB_API_KEY"
```

Without `month`, the current month so far. Each part's `status` is `measured`, `not_connected` or `unavailable`.

## Export the inventory

`GET /api/v1/catalog/exports/inventory` returns every asset not gone, with its application, service, owner team, tier, availability target and RACI: at most 50,000 rows, with `truncated` when there were more. Teams and people are their IDs.

## Cloud accounts

Connections are created first and given access second: `POST /api/v1/catalog/connections` returns the connection with its `external_id`; then, for AWS, download the role template from `GET /api/v1/catalog/connections/{id}/template`, create the role, and `PATCH` its `role_arn`. `POST /api/v1/catalog/connections/{id}/check` tries the access and answers `{"ok": true, "account_id": "…"}` or `{"ok": false, "error": "…"}`. See [Cloud accounts](https://docs.evohub.io/catalog-cloud-accounts.md).

## Things to know

- **Agents only.** The routes under `/api/v1/catalog/ingest/` are for the EvoHub agent, which signs each request with its own key. An API key cannot send inventory, and an agent cannot read.
- **Observations are read-only.** Resources and applications are never created or edited through the API; only which service they belong to is decided here. Sources have no create or delete either: an agent's appears when it first reports, and is retired by revoking the agent.
- **Parts you cannot read are left out.** A service's detail omits `observations` for a key without `catalog:resource:read`; a resource's or application's omits `proposals` without `catalog:component:read`; a cloud account's `external_id` is empty without `identity:cloudaccount:write`.
- **Strict bodies.** A field an endpoint does not know is refused with `INVALID_BODY`, so a typo never reads as success.
- **Errors.** Branch on `error.code`, for example `NOT_FOUND`, `SLUG_TAKEN`, `SLUG_IMMUTABLE`, `MATCH_DECIDED` or `ENVIRONMENT_IN_USE`. The general rules are in [Errors](https://docs.evohub.io/errors.md).

## Related

- [Catalog API reference](https://docs.evohub.io/catalog.md)
- [API overview](https://docs.evohub.io/api-overview.md)
- [API keys and scopes](https://docs.evohub.io/api-keys-and-scopes.md)
- [Catalog overview](https://docs.evohub.io/catalog-overview.md)
