# BYOC clusters (/api-v2/byoc-clusters)



BYOC is the escalation path activated only where replay proves value. This control-plane registry records the customer-cloud clusters a project operates (region, runtime stack, orchestrator, KV-cache layer, autoscaling envelope) so the broker can route to them and the console can dashboard them. The control plane never holds GPUs; the data plane itself is deployed from the Helm charts under `infra/byoc/` and reports health back via heartbeats. Cluster ids are prefixed `byc_`. See [BYOC execution](/execution/byoc) and the [BYOC stack](/infrastructure/byoc-stack).

All requests require a bearer API key. See [authentication](/api-reference/authentication).

## Register a cluster [#register-a-cluster]

`POST /v2/byoc/clusters`

<ParamField body="name" type="string">
  A human label for the cluster.
</ParamField>

<ParamField body="region" type="string">
  The region the cluster runs in, e.g. `us`.
</ParamField>

<ParamField body="runtime" type="string" default="sglang+flashinfer">
  The runtime lane, e.g. `sglang+flashinfer`, `llm-d+vllm`, `trtllm`.
</ParamField>

<ParamField body="kv_cache" type="string" default="lmcache+mooncake">
  The KV-cache management layer.
</ParamField>

<ParamField body="orchestrator" type="string" default="dynamo">
  The orchestrator, e.g. `dynamo` or `aibrix`.
</ParamField>

<ParamField body="endpoint" type="string">
  The customer-cloud data-plane endpoint the broker dispatches to.
</ParamField>

<ParamField body="autoscaling" type="object">
  The autoscaling envelope. `min_replicas` must be at most `max_replicas`, and `max_replicas` must be greater than zero. Defaults to `{ "min_replicas": 1, "max_replicas": 4, "target_ttft_ms": 500 }`.

  <Expandable title="autoscaling">
    <ParamField body="min_replicas" type="integer">
      Minimum replicas.
    </ParamField>

    <ParamField body="max_replicas" type="integer">
      Maximum replicas.
    </ParamField>

    <ParamField body="target_ttft_ms" type="integer">
      The TTFT SLA the autoscaler holds.
    </ParamField>
  </Expandable>
</ParamField>

```bash
curl https://api.zumik.ai/v2/byoc/clusters \
  -H "Authorization: Bearer $ZUMIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "us-east hot lane",
    "region": "us",
    "autoscaling": { "min_replicas": 1, "max_replicas": 8, "target_ttft_ms": 400 }
  }'
```

```json
{
  "id": "byc_01jy7nuv23w5x6y7z8a9b0c1d4",
  "object": "byoc_cluster",
  "project_id": "prj_01jy7n0a4c8m2t6v9q3wrxk7bd",
  "name": "us-east hot lane",
  "region": "us",
  "status": "registering",
  "runtime": "sglang+flashinfer",
  "kv_cache": "lmcache+mooncake",
  "orchestrator": "dynamo",
  "endpoint": null,
  "autoscaling": { "min_replicas": 1, "max_replicas": 8, "target_ttft_ms": 400 },
  "created_at": "2026-06-15T16:48:18Z",
  "updated_at": "2026-06-15T16:48:18Z",
  "last_heartbeat_at": null
}
```

<ResponseField name="id" type="string">
  Opaque cluster id, prefixed `byc_`.
</ResponseField>

<ResponseField name="object" type="string">
  Always `byoc_cluster`.
</ResponseField>

<ResponseField name="project_id" type="string">
  The owning project.
</ResponseField>

<ResponseField name="name" type="string">
  The cluster label.
</ResponseField>

<ResponseField name="region" type="string">
  The cluster region.
</ResponseField>

<ResponseField name="status" type="string">
  `registering`, `active`, `draining`, or `down`. A freshly registered cluster starts at `registering`.
</ResponseField>

<ResponseField name="runtime" type="string">
  The runtime lane.
</ResponseField>

<ResponseField name="kv_cache" type="string">
  The KV-cache layer.
</ResponseField>

<ResponseField name="orchestrator" type="string">
  The orchestrator.
</ResponseField>

<ResponseField name="endpoint" type="string">
  The data-plane endpoint, or 

  `null`

  .
</ResponseField>

<ResponseField name="autoscaling" type="object">
  The autoscaling envelope.
</ResponseField>

<ResponseField name="created_at" type="string">
  RFC 3339 timestamp.
</ResponseField>

<ResponseField name="updated_at" type="string">
  RFC 3339 timestamp.
</ResponseField>

<ResponseField name="last_heartbeat_at" type="string">
  When the cluster last reported, or 

  `null`

  .
</ResponseField>

## List clusters [#list-clusters]

`GET /v2/byoc/clusters`

```bash
curl https://api.zumik.ai/v2/byoc/clusters \
  -H "Authorization: Bearer $ZUMIK_API_KEY"
```

Returns `{ "object": "list", "data": [ ... ] }` of cluster objects for the project.

## Retrieve a cluster [#retrieve-a-cluster]

`GET /v2/byoc/clusters/{cluster_id}`

<ParamField path="cluster_id" type="string">
  The `byc_...` id to fetch.
</ParamField>

```bash
curl https://api.zumik.ai/v2/byoc/clusters/byc_01jy7nuv23w5x6y7z8a9b0c1d4 \
  -H "Authorization: Bearer $ZUMIK_API_KEY"
```

Returns the cluster object.

## Heartbeat [#heartbeat]

`POST /v2/byoc/clusters/{cluster_id}/heartbeat`

The BYOC operator posts a heartbeat to advance the cluster status and prove liveness. The first `active` heartbeat moves a `registering` cluster to `active`.

<ParamField path="cluster_id" type="string">
  The `byc_...` id to update.
</ParamField>

<ParamField body="status" type="string">
  The reported data-plane status: `active`, `draining`, or `down`.
</ParamField>

```bash
curl https://api.zumik.ai/v2/byoc/clusters/byc_01jy7nuv23w5x6y7z8a9b0c1d4/heartbeat \
  -H "Authorization: Bearer $ZUMIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "active" }'
```

Returns the cluster object with the updated `status` and `last_heartbeat_at`.

## Deregister a cluster [#deregister-a-cluster]

`DELETE /v2/byoc/clusters/{cluster_id}`

<ParamField path="cluster_id" type="string">
  The `byc_...` id to deregister.
</ParamField>

```bash
curl -X DELETE https://api.zumik.ai/v2/byoc/clusters/byc_01jy7nuv23w5x6y7z8a9b0c1d4 \
  -H "Authorization: Bearer $ZUMIK_API_KEY"
```

```json
{
  "id": "byc_01jy7nuv23w5x6y7z8a9b0c1d4",
  "object": "byoc_cluster.deregistered",
  "deleted": true
}
```

## HiCache activation plan [#hicache-activation-plan]

`POST /v2/byoc/hicache-plan`

Decide whether serving a cached prefix from a cache tier beats recomputing it on the GPU, per the §18.6 activation rule (`expected_recompute_cost > lookup + transfer + decompression + queue_delay`). Feed it replay-measured costs (in ms of TTFT) and it returns the cheapest viable tier, a per-tier breakdown, and a recommendation. Run this before turning on `hicache.*` in the [BYOC stack](/infrastructure/byoc-stack). Pure planning - it holds no GPUs and stores nothing.

<ParamField body="expected_recompute_ms" type="number">
  Expected GPU cost to recompute the prefix, in ms - the left side of the inequality.
</ParamField>

<ParamField body="tiers" type="array">
  Candidate cache tiers and their measured fetch costs (1-16 entries).

  <Expandable title="tier">
    <ParamField body="name" type="string">
      Tier label, e.g. 

      `gpu_hbm`

      , 

      `host_ram`

      , 

      `local_nvme`

      , 

      `remote`

      .
    </ParamField>

    <ParamField body="lookup_ms" type="number">
      Index/lookup cost.
    </ParamField>

    <ParamField body="transfer_ms" type="number">
      Transfer cost to the GPU.
    </ParamField>

    <ParamField body="decompression_ms" type="number">
      Decompression cost.
    </ParamField>

    <ParamField body="queue_delay_ms" type="number">
      Queue-delay cost under load.
    </ParamField>
  </Expandable>
</ParamField>

```bash
curl https://api.zumik.ai/v2/byoc/hicache-plan \
  -H "Authorization: Bearer $ZUMIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "expected_recompute_ms": 40,
    "tiers": [
      { "name": "host_ram",   "lookup_ms": 1, "transfer_ms": 3,  "decompression_ms": 0.5, "queue_delay_ms": 1 },
      { "name": "local_nvme", "lookup_ms": 2, "transfer_ms": 12, "decompression_ms": 3,   "queue_delay_ms": 4 }
    ]
  }'
```

```json
{
  "object": "hicache_plan",
  "expected_recompute_ms": 40,
  "decision": { "decision": "fetch", "tier": "host_ram", "saved_ms": 34.5 },
  "tiers": [
    { "name": "host_ram",   "total_ms": 5.5,  "beats_recompute": true, "saved_ms": 34.5 },
    { "name": "local_nvme", "total_ms": 21.0, "beats_recompute": true, "saved_ms": 19.0 }
  ],
  "recommendation": "activate_tier"
}
```

<ResponseField name="decision" type="object">
  `{ "decision": "fetch", "tier": "...", "saved_ms": N }` for the cheapest viable tier, or `{ "decision": "recompute" }` when no tier beats recompute.
</ResponseField>

<ResponseField name="tiers" type="array">
  Per-tier verdicts: `total_ms`, `beats_recompute`, and `saved_ms` (negative when the fetch loses).
</ResponseField>

<ResponseField name="recommendation" type="string">
  `activate_tier` (turn on `hicache.enabled`) or `recompute_only` (leave it off for this workload).
</ResponseField>

## Errors [#errors]

| Status | Code                    | When                                                                                                                                                                   |
| ------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | `invalid_request_error` | Empty `name`/`region`, an invalid autoscaling envelope, a heartbeat `status` outside `active`/`draining`/`down`, or a `hicache-plan` with no tiers / non-finite costs. |
| 401    | `invalid_api_key`       | Missing or invalid API key.                                                                                                                                            |
| 404    | `invalid_request_error` | The cluster does not exist in this project.                                                                                                                            |

See the full table on [errors](/api-reference/errors).
