**Language / Язык:** [English](api-surface.md) | [Русский](ru/api-surface.md)

# API surface

## Request path

1. Middleware assigns or forwards a request ID (`REQUEST_ID_HEADER`).
2. FastAPI routes the request to the vSphere REST router (`/api`, `/rest`),
   the SOAP router (`/sdk`), or (if `ENABLE_PVE_STUB=true`) the optional
   legacy stub.
3. `/api/session` (or `/rest/com/vmware/cis/session`, or SOAP `Login`)
   resolves a principal and issues a `vmware-api-session-id`.
4. `require_read` / `require_privilege(...)` dependencies check the session's
   roles before revealing or mutating resources.
5. A deep handler (core inventory/lifecycle/tagging/content/appliance logic)
   or the DB-backed stub surface executes against PostgreSQL-backed state.
6. Long operations (power, clone, relocate, snapshot, OVF deploy) create a
   durable CIS task and return `{ "task": "task-…" }`.

## Two REST surfaces on one registry

- **Core (deep) handlers** — ~104 verb+path combinations across
  [`app/vsphere/rest/router.py`](../app/vsphere/rest/router.py),
  `vm_ext.py`, `inventory_ext.py`, `platform_rest.py`, `tagging_rest.py`,
  `content_rest.py`, `appliance_ext.py`, `nfc_rest.py`, `tasks.py`. These read
  and mutate the seeded inventory/tagging/content/appliance tables directly.
- **DB-backed stub surface** —
  [`app/vsphere/rest/stub_surface.py`](../app/vsphere/rest/stub_surface.py)
  answers the remaining Broadcom Automation API operations index routes
  (registered from `universe.json`) against `vsphere_api_state`. GET returns
  live inventory-derived payloads when possible, otherwise seeded rows;
  PUT/PATCH persist into `vsphere_api_state`; POST appends collection rows;
  DELETE removes them. No `"stub": true` marker is returned — probes see real
  seeded payloads.

Both surfaces share one route table; core handlers take priority over stub
entries registered for the same verb+path.

## Legacy `/rest`

[`app/vsphere/rest/legacy.py`](../app/vsphere/rest/legacy.py) wraps
vm/host/datastore/network/datacenter/cluster/power/appliance reads (and VM
power) in `{ "value": … }` envelopes for older `com.vmware.vcenter.*` clients.

## Errors ([`app/vsphere/errors.py`](../app/vsphere/errors.py))

| Status | `error_type` | Typical cause |
|---|---|---|
| 400 | `invalid_argument` / `already_exists` | Malformed body, duplicate name |
| 401 | `unauthenticated` | Missing/invalid/expired session |
| 403 | `unauthorized` | Session lacks the required privilege |
| 404 | `not_found` | Unknown MOID/path parameter |
| 409 | (handler-specific) | Illegal power-state transition, lock conflict |
| 501 | `error` | Only reachable via the optional legacy stub's undeclared-method fallback |

All error bodies follow the vSphere Automation shape:
`{ "error_type": "...", "messages": [{ "default_message": "...", "id": "...", "args": [] }] }`.

## Tasks

Async work (power, clone, snapshot, relocate, OVF deploy, guest customize)
returns a task id. Poll:

```text
GET /api/cis/tasks/{task}
```

Task rows commit in `vsphere_tasks`; `progress` is `100` once `status` is
`SUCCEEDED`/`FAILED`. HTTP 200/201 on the mutation request means "accepted",
not "VM already in final state". See [Tasks](domains/tasks.md).

## Exploration

- Interactive FastAPI docs: `/docs`
- Web UI method inspector: `/` → catalog → method
- UI helper APIs: `/ui/api/catalog`, `/ui/api/method`, `/ui/api/compatibility`
- Coverage registry: [`app/vsphere/rest/coverage.py`](../app/vsphere/rest/coverage.py)
- Path-floor / catalog matrix: [`app/vsphere/contracts/matrix.py`](../app/vsphere/contracts/matrix.py)

## Compatibility endpoints

| Path | Format |
|---|---|
| `/ui/api/compatibility?major=N` | JSON |

See [Compatibility](compatibility.md) and [API coverage](api-coverage.md) for
the full Broadcom-universe-vs-implemented breakdown.
