> ## Documentation Index
> Fetch the complete documentation index at: https://internal.september.wtf/llms.txt
> Use this file to discover all available pages before exploring further.

# Admin endpoints

> Register products and update policies. Requires X-Admin-Key.

Two endpoints, both behind `X-Admin-Key` (set via `ORCH_ADMIN_KEY`).
These are the only endpoints that don't use a product key — they
*create* products.

## POST /products/register

Register a new product. Returns the platform API key — save it.

### Request

```http theme={null}
POST /products/register
X-Admin-Key: <admin key>
Content-Type: application/json

{
  "slug": "demo",
  "display_name": "Demo product",
  "policy": {
    "max_engines": 1000,
    "rate_limit_rpm": 600
  }
}
```

| Field          | Type   | Required | Purpose                                                                |
| -------------- | ------ | -------- | ---------------------------------------------------------------------- |
| `slug`         | string | yes      | Unique slug (e.g. `bap`, `demo`). Used in audit and logs.              |
| `display_name` | string | yes      | Human-readable name.                                                   |
| `policy`       | object | no       | Initial policy. Default: `{}` (no limits). See [Policy](../../policy). |

### Response (200)

```json theme={null}
{
  "product_id": "5c2f...",
  "slug": "demo",
  "platform_api_key": "pk-sept-..."
}
```

The plaintext key is returned **once**. The orchestrator stores only
the SHA-256 hash. If you lose it, you have to re-register the product.

### Errors

* `401 INVALID_ADMIN_KEY` — wrong or missing `X-Admin-Key`.
* `409 PRODUCT_SLUG_EXISTS` — slug already in use.

## PUT /products/{product_id}/policy

Update a product's policy. Used to raise/lower limits without
re-registering.

### Request

```http theme={null}
PUT /products/5c2f.../policy
X-Admin-Key: <admin key>
Content-Type: application/json

{
  "policy": {
    "max_engines": 5000,
    "rate_limit_rpm": 2000
  }
}
```

| Field    | Type   | Required | Purpose                                |
| -------- | ------ | -------- | -------------------------------------- |
| `policy` | object | yes      | Replaces the existing policy entirely. |

### Response (200)

```json theme={null}
{
  "policy": {
    "max_engines": 5000,
    "rate_limit_rpm": 2000
  }
}
```

The change takes effect immediately on the next request. Existing
engines exceeding a tightened `max_engines` are NOT destroyed
retroactively.

### Errors

* `401 INVALID_ADMIN_KEY` — wrong or missing `X-Admin-Key`.
* `404 PRODUCT_NOT_FOUND` — product\_id doesn't exist.

## What admin endpoints can't do

* **Destroy a product.** No DELETE today. To remove a product:
  destroy all its engines, then delete the row directly:
  `DELETE FROM products WHERE id = '...';`. Cascades clean up
  engines.
* **Rotate a platform key.** No endpoint today. Re-register the
  product or update `products.api_key_hash` directly.
* **Upgrade engine version per product.** Engines run on
  `ORCH_ENGINE_IMAGE` globally. To run different versions per
  product, you need separate orchestrator deployments.

## Security

The admin key is a single secret per orchestrator deployment. Treat
it like a database master password:

* Keep it in your secret manager.
* Audit access to it.
* Rotate quarterly.
* Don't share with products' application code.

For day-to-day product operations, the platform key is enough — admin
endpoints are deliberately rare.

## See also

* [Auth](../auth) — `X-Admin-Key` mechanics.
* [Policy](../../policy) — what the policy fields mean.
* [Security](../../security) — admin key rotation playbook.
