> ## 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

Creates a new encrypted index on the service and returns an `EncryptedIndex` bound to it.

```typescript theme={null}
async createIndex({
    indexName: string,
    indexKey?: Uint8Array,
    kmsName?: string,
    dimension?: number,
    metric?: 'euclidean' | 'squared_euclidean' | 'cosine',
    embeddingModel?: string,
    storagePrecision?: 'float32' | 'float16' | 'tq12' | 'tq8' | 'tq6' | 'tq4',
    metadataSchema?: { [field: string]: MetadataFieldPolicy },
    textFields?: string[],
    bm25K1?: number,
    bm25B?: number,
}): Promise<EncryptedIndex>
```

<Note>There is one index type, configured with flat fields. The earlier `IndexIVFFlat`, `IndexIVFPQ`, and `IndexIVFSQ` types and the `indexConfig` argument no longer exist.</Note>

### Parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `indexName` | `string` | - | Unique name for the index. |
| `indexKey` | `Uint8Array` | `undefined` | *(Optional)* 32-byte encryption key (SDK-supplied KEK). Pass either `indexKey` or `kmsName`, never both. |
| `kmsName` | `string` | `undefined` | *(Optional)* Name of a `kms.registry` entry in the service YAML. The service generates the KEK and wraps it with that provider; the SDK never sees the plaintext key. |
| `dimension` | `number` | `undefined` | *(Optional)* Vector dimensionality. When omitted, the service detects it from the first upsert. |
| `metric` | `string` | `undefined` (service default: `'euclidean'`) | *(Optional)* `'euclidean'`, `'squared_euclidean'`, or `'cosine'`. |
| `embeddingModel` | `string` | `undefined` | *(Optional)* Name of one of the service's built-in embedding models (listed by `GET /v1/embedding-models`), used for automatic embedding generation. When set, `dimension` is taken from the model. |
| `storagePrecision` | `string` | `undefined` (service default: `'float32'`) | *(Optional)* Storage format for the index's stored vector embeddings. See [Storage precision](#storage-precision). |
| `metadataSchema` | `{ [field: string]: MetadataFieldPolicy }` | `undefined` | *(Optional)* Per-field metadata indexing policy. Fixed at creation and cannot be changed later. See [Metadata schema](#metadata-schema). |
| `textFields` | `string[]` | `undefined` | *(Optional)* Metadata fields to index for BM25 full-text search. Shorthand for `{ fullText: true }` in `metadataSchema`. Enables `text` on [`query()`](../encrypted-index/query) and [`queryMetadata()`](../encrypted-index/query-metadata). |
| `bm25K1` | `number` | `undefined` (service default: `1.2`) | *(Optional)* BM25 term-frequency saturation, `>= 0`. Requires at least one full-text field. |
| `bm25B` | `number` | `undefined` (service default: `0.75`) | *(Optional)* BM25 length-normalization strength, in `[0, 1]`. Requires at least one full-text field. |

### Key management

Pass exactly one of `indexKey` or `kmsName`. The service rejects both together with `CyborgDBValidationError` (HTTP `400`), whatever the KMS provider.

* **SDK-supplied KEK:** pass `indexKey`. Supply the same key again on every [`loadIndex()`](./load-index).
* **KMS-backed:** pass `kmsName`. The service generates the key, wraps it with the named provider, and stores the wrapped key. Later calls omit `indexKey`.

### Storage precision

`storagePrecision` sets the format used to store the index's vector embeddings. Every value works with every metric.

| Value | Bits per dimension |
| - | - |
| `'float32'` | 32 |
| `'float16'` | 16 |
| `'tq12'` | 12 |
| `'tq8'` | 8 |
| `'tq6'` | 6 |
| `'tq4'` | 4 |

The `tq*` values are TurboQuant tiers; lower tiers use less storage in exchange for a small recall and latency cost.

### Metadata schema

`metadataSchema` sets how each metadata field is indexed. Metadata itself stays schemaless: this controls indexing, not what may be stored. Fields left out get the default policy (indexed and filterable). Nested fields use dot-paths such as `'loc.city'`.

```typescript theme={null}
const metadataSchema = {
    title:        { filterable: true, pattern: true },  // regex-searchable
    internalBlob: { filterable: false },                // stored, not indexed
    body:         { fullText: true },                   // BM25 full-text search
};
```

Each field takes three settings (the [`MetadataFieldPolicy`](../types#metadatafieldpolicy) type):

| Setting | Default | Effect |
| - | - | - |
| `filterable` | `true` | Indexes the field, so filters on it resolve from the index. `false` still stores the field but doesn't index it; see [Filters the index cannot answer](#filters-the-index-cannot-answer). |
| `pattern` | `false` | Also builds a regex dictionary, so `$regex` and `$contains` resolve from the index. Requires `filterable: true`. |
| `fullText` | `false` | Sends the field's text through the BM25 analyzer instead of exact-match indexing. Implies `filterable: false`, and can't be combined with `pattern: true`. |

#### Filters the index cannot answer

The index can answer a filter when the field is filterable and, for `$regex` or `$contains`, the field also has `pattern` set. Any other filter is handled differently depending on the call:

| Call | Filter the index can't answer |
| - | - |
| [`query()`](../encrypted-index/query) without `text` | Still works, but slower: the filter is applied to the decrypted metadata after the search. |
| [`query()`](../encrypted-index/query) with `text` | Rejected with `CyborgDBValidationError`. |
| [`queryMetadata()`](../encrypted-index/query-metadata) | Rejected with `CyborgDBValidationError`. |

With the schema above:

```typescript theme={null}
{ title: { $regex: '^Intro' } }    // answerable: title is filterable and has pattern
{ internalBlob: 'abc' }            // not answerable: internalBlob isn't filterable
{ author: { $regex: '^Ann' } }     // not answerable: author is filterable by default, but has no pattern
```

The stored policy is returned by the [`metadataSchema()`](../getters#metadataschema) getter.

### Returns

`Promise<EncryptedIndex>` bound to the new index. For KMS-backed indexes it holds no plaintext key.

### Exceptions

<AccordionGroup>
  <Accordion title="CyborgDBValidationError">
    * Neither `indexKey` nor `kmsName` is passed, or `indexKey` is not 32 bytes. Thrown before any request is sent.
    * The service rejects the request (HTTP `400`/`422`): the index name already exists, both `indexKey` and `kmsName` were passed, the `kmsName` slot is unknown, `storagePrecision` isn't one of the six values, or `metadataSchema` combines settings that aren't allowed.
  </Accordion>

  <Accordion title="CyborgDBAuthenticationError">
    The client's `apiKey` is missing or invalid (HTTP `401`), or is a per-user key: creating an index requires the root key (HTTP `403`).
  </Accordion>

  <Accordion title="CyborgDBServiceError">
    The service returns `5xx`, including KMS wrap failures (`502`).
  </Accordion>

  <Accordion title="CyborgDBTransportError">
    The service can't be reached.
  </Accordion>
</AccordionGroup>

See [Errors](../errors) for the full hierarchy.

### Example Usage

#### SDK-supplied key

```typescript theme={null}
import { Client } from 'cyborgdb';

const client = new Client({ baseUrl: 'http://localhost:8000', apiKey: 'your-api-key' });

const indexKey = Client.generateKey();

// The dimension is detected from the first upsert
const index = await client.createIndex({ indexName: 'documents', indexKey });
```

<Note>Store `indexKey` securely: it is needed to load the index again. See [Generate Key](./generate-key).</Note>

#### Explicit dimension and metric

```typescript theme={null}
const index = await client.createIndex({
    indexName: 'documents_384',
    indexKey,
    dimension: 384,
    metric: 'cosine',
});
```

#### KMS-backed index

```typescript theme={null}
// The service generates the key and wraps it with the 'prod-aws' registry slot.
const index = await client.createIndex({
    indexName: 'kms_documents',
    kmsName: 'prod-aws',
    dimension: 384,
});
// Later calls, including loadIndex(), omit indexKey.
```

#### Automatic embeddings

```typescript theme={null}
// The dimension is taken from the embedding model
const index = await client.createIndex({
    indexName: 'semantic_docs',
    indexKey,
    embeddingModel: 'all-MiniLM-L6-v2',
});
```

See [Auto-Generate Embeddings](../../guides/data-operations/add-items#automatic-embedding-generation).

#### Reduced storage with TurboQuant

```typescript theme={null}
const index = await client.createIndex({
    indexName: 'compact_docs',
    indexKey,
    dimension: 768,
    storagePrecision: 'tq8',
});
```

#### Per-field metadata policy

```typescript theme={null}
const index = await client.createIndex({
    indexName: 'docs_index',
    indexKey,
    dimension: 4,
    metadataSchema: {
        title:        { filterable: true, pattern: true },
        internalBlob: { filterable: false },
    },
});
await index.upsert({
    items: [
        { id: 'doc1', vector: [0.1, 0.2, 0.3, 0.4], metadata: { title: 'intro to encryption' } },
        { id: 'doc2', vector: [0.5, 0.6, 0.7, 0.8], metadata: { title: 'advanced setup' } },
    ],
});

// $regex resolves from the index because 'title' has pattern: true
console.log(await index.queryMetadata({ filters: { title: { $regex: '^intro' } } }));
// Output: [ { id: 'doc1' } ]
```

#### Full-text search fields

```typescript theme={null}
const index = await client.createIndex({
    indexName: 'articles',
    indexKey,
    dimension: 4,
    textFields: ['body'],
});
console.log(await index.bm25());
// Output:
// {
//   k1: 1.2,
//   b: 0.75,
//   analyzerVersion: 'en/1;sw=2de831ef7be676b6;snowball-english-utf8-3.0.1'
// }
```


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