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

# Create Index

Create a new encrypted index.

<Note>There is a single index type. Configuration parameters (`dimension`, `metric`, `embedding_model`, `storage_precision`) are top-level fields; the `index_config` object and the IVFFlat/IVFPQ/IVFSQ distinction from earlier versions no longer apply.</Note>

## Authentication

Required - **admin (root) API key** via `X-API-Key` header. Per-user (`cdbk_`) keys are rejected with `403` — a per-user credential operates on data, not index lifecycle:

```http theme={null}
X-API-Key: cyborg_your_api_key_here
```

When RBAC is not enabled (`CYBORGDB_SERVICE_ROOT_KEY` unset), no header is required.

## Request Body

```json theme={null}
{
  "index_name": "my_index",
  "index_key": "64_character_hex_string_representing_32_bytes",
  "dimension": 384,
  "metric": "euclidean",
  "embedding_model": "all-MiniLM-L6-v2",
  "storage_precision": "float32",
  "metadata_schema": {
    "title": {"filterable": true, "pattern": true},
    "internal_blob": {"filterable": false}
  }
}
```

<Expandable title="parameters">
  <ParamField body="index_name" type="string" required="true">
    Unique name for the index.
  </ParamField>

  <ParamField body="index_key" type="string">
    32-byte encryption key as a hex string. Required when `kms_name` is omitted (SDK-supplied KEK path). Must be omitted when `kms_name` is set — supplying both is always rejected with 400.
  </ParamField>

  <ParamField body="kms_name" type="string">
    Name of a `kms.registry` entry in the service YAML configuration. When supplied, the service generates a fresh KEK, wraps it under the named provider (e.g. `aws-kms`), and persists the envelope. The SDK never sees the plaintext key. Mutually exclusive with `index_key`.
  </ParamField>

  <ParamField body="dimension" type="integer">
    Vector dimensionality. If omitted, the server auto-detects from the first upsert.
  </ParamField>

  <ParamField body="metric" type="string">
    Distance metric. Options: `euclidean`, `squared_euclidean`, `cosine`. Defaults to the server's default (`euclidean`).
  </ParamField>

  <ParamField body="embedding_model" type="string">
    Optional embedding model name for automatic embedding generation on upsert/query. Must be one of the built-in models returned by [List Embedding Models](./list-embedding-models). When set, `dimension` is inferred from the model and may be omitted.
  </ParamField>

  <ParamField body="storage_precision" type="string">
    On-disk rerank-vector dtype. Accepts `"float32"` (default), `"float16"`, or the TurboQuant tiers `"tq12"`, `"tq8"`, `"tq6"`, `"tq4"`. Lower precisions reduce storage at the cost of small recall — `"float16"` halves it; the `tq*` tiers compress further (12, 8, 6, or 4 bits per component).
  </ParamField>

  <ParamField body="metadata_schema" type="object">
    Per-field metadata indexing policy, keyed by field name (dot-paths for nested fields). Fixed at create time and immutable. Fields omitted here inherit the default (indexed and filterable). See [Metadata Schema](#metadata-schema) below.

    <Expandable title="MetadataFieldPolicy">
      <ParamField body="filterable" type="boolean" default={true}>
        Index the field so filters on it resolve from the index. When `false`, the field is still stored and still filterable via post-filter on `/v1/vectors/query`, but cannot be filtered on `/v1/vectors/query_metadata`.
      </ParamField>

      <ParamField body="pattern" type="boolean" default={false}>
        Additionally build the field's regex dictionary, needed for index-resolved `$regex` / `$contains`. Requires `filterable=true`.
      </ParamField>

      <ParamField body="full_text" type="boolean" default={false}>
        Route the field's string value through the BM25 analyzer instead of exact-match indexing, making it searchable via `text` on `/v1/vectors/query` and `/v1/vectors/query_metadata`. A field is either analyzed or exact-match indexed, so `full_text: true` implies `filterable: false` and is incompatible with `pattern: true`.
      </ParamField>
    </Expandable>
  </ParamField>

  <ParamField body="text_fields" type="array[string]">
    Fields to index for BM25 full-text search — shorthand for marking them `full_text: true` in `metadata_schema`. Enables `text` queries on `/v1/vectors/query` and `/v1/vectors/query_metadata`.
  </ParamField>

  <ParamField body="bm25_k1" type="number">
    BM25 term-frequency saturation (`>= 0`, default `1.2`). Requires at least one `full_text` field.
  </ParamField>

  <ParamField body="bm25_b" type="number">
    BM25 length-normalization strength (in `[0, 1]`, default `0.75`). Requires at least one `full_text` field.
  </ParamField>
</Expandable>

## Key Management Modes

Exactly one of `index_key` / `kms_name` must be supplied:

* **SDK-supplied KEK** (set `index_key`, omit `kms_name`) — the service records the index with `provider: none`. The same key must be re-supplied on every subsequent request (load, query, upsert, etc.).
* **KMS-backed** (set `kms_name`, omit `index_key`) — the service generates the KEK, wraps it under the named registry slot, and persists the envelope. Subsequent requests omit `index_key` entirely; the service resolves it from the index's stored key envelope.

Supplying both is rejected with 400, regardless of the registry slot's provider type. (The `none` provider is not addressable by name — omit `kms_name` to use the SDK-supplied path.)

## Metadata Schema

`metadata_schema` sets the per-field indexing policy. Metadata itself stays schemaless — this controls how each field is indexed, not what may be stored. Three knobs per field:

* `filterable` (default `true`) — index the field so filters on it resolve from the index (pre-filter, and the only path `/v1/vectors/query_metadata` uses). `false` still stores the field but forces the dense post-filter path when `/v1/vectors/query` filters reference it — cheaper writes, slower filtered queries.
* `pattern` (default `false`) — additionally build the field's regex dictionary, needed for index-resolved `$regex` / `$contains`. Requires `filterable: true`.
* `full_text` (default `false`) — route the field through the BM25 analyzer for full-text search instead of exact-match indexing. Implies `filterable: false` and is incompatible with `pattern: true`.

On `/v1/vectors/query` the schema is a performance hint: filters that miss the index fall back to a post-filter over the decrypted metadata, same rows either way. On `/v1/vectors/query_metadata` it is enforced — `$regex` / `$contains` on a non-`pattern` field, or any filter on a `filterable: false` field, is rejected with 400.

The recorded policy is echoed back on `POST /v1/indexes/describe` under `metadata_schema` (empty object when the index uses the default posture).

## Response

```json theme={null}
{
  "status": "success",
  "message": "Index 'my_index' created successfully"
}
```

## Exceptions

<AccordionGroup>
  <Accordion title="400 Bad Request">
    Index name already exists. Also raised for missing/conflicting key parameters (both `index_key` and `kms_name` supplied, or neither), and for BM25 parameter violations: `bm25_k1` / `bm25_b` supplied for an index with no `full_text` field, `bm25_k1 < 0`, `bm25_b` outside `[0, 1]`, or a `text_fields` entry naming a field the schema sets `full_text=false`
  </Accordion>

  <Accordion title="401 Unauthorized">
    Authentication failed (invalid API key)
  </Accordion>

  <Accordion title="403 Forbidden">
    Authenticated with a per-user (`cdbk_`) key — this operation requires the admin (root) API key
  </Accordion>

  <Accordion title="422 Unprocessable Entity">
    Invalid request parameters — including `metadata_schema` with `pattern=true` but `filterable=false`, `full_text=true` combined with `pattern=true` or an explicit `filterable=true`, an empty field name, or an empty `text_fields` entry
  </Accordion>

  <Accordion title="500 Internal Server Error">
    Internal server error
  </Accordion>

  <Accordion title="502 Bad Gateway">
    KMS error (e.g. wrap operation failed)
  </Accordion>
</AccordionGroup>

## Example Usage

**Basic (SDK-supplied key):**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/indexes/create" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "my_index",
       "index_key": "your_64_character_hex_key_here",
       "dimension": 384
     }'
```

**KMS-backed:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/indexes/create" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "kms_index",
       "kms_name": "prod-aws",
       "dimension": 384
     }'
```

**With auto-embedding:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/indexes/create" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "semantic_index",
       "index_key": "your_64_character_hex_key_here",
       "embedding_model": "all-MiniLM-L6-v2"
     }'
```

**Float16 storage for reduced footprint:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/indexes/create" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "compact_index",
       "index_key": "your_64_character_hex_key_here",
       "dimension": 768,
       "storage_precision": "float16"
     }'
```

**With per-field metadata policy:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/indexes/create" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "docs_index",
       "index_key": "your_64_character_hex_key_here",
       "dimension": 384,
       "metadata_schema": {
         "title": {"filterable": true, "pattern": true},
         "internal_blob": {"filterable": false}
       }
     }'
```

<Note>
  When `embedding_model` is specified, `dimension` is optional — the server infers it from the model. The index will automatically generate embeddings for text content during upsert operations.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.