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

# Upsert Vectors

Add new vectors or update existing ones in the encrypted index.

## Authentication

Required - API key via `X-API-Key` header:

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

Any valid credential is accepted — the root key or a per-user `cdbk_` token. 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",
  "items": [
    {
      "id": "item_1",
      "vector": [0.1, 0.2, 0.3, 0.4],
      "contents": "Hello world!",
      "metadata": {"category": "greeting", "language": "en"}
    },
    {
      "id": "item_2",
      "vector": [0.5, 0.6, 0.7, 0.8],
      "contents": "Bonjour monde!",
      "metadata": {"category": "greeting", "language": "fr"}
    }
  ]
}
```

<Expandable title="parameters">
  <ParamField body="index_name" type="string" required="true">
    Name of the target index
  </ParamField>

  <ParamField body="index_key" type="string">
    32-byte encryption key as a hex string. Required for indexes created with the SDK-supplied KEK path; omit for KMS-backed indexes (the service resolves the key from the index's stored key envelope).
  </ParamField>

  <ParamField body="items" type="array[object]" required="true">
    List of items to upsert, each containing:

    <Expandable title="items parameters">
      <ParamField body="id" type="string" required="true">
        Unique identifier for the item
      </ParamField>

      <ParamField body="vector" type="array[number]" required="false">
        Vector embedding (required if index does not use automatic embedding generation)
      </ParamField>

      <ParamField body="contents" type="string">
        Text content associated with the vector
      </ParamField>

      <ParamField body="metadata" type="object">
        Key-value pairs for additional metadata. String values in fields the index designates `full_text` are BM25-indexed on ingest — see [Full-text (BM25) ingestion](#full-text-bm25-ingestion).
      </ParamField>
    </Expandable>
  </ParamField>
</Expandable>

## Full-Text (BM25) Ingestion

There is no separate ingestion call or parameter for BM25: text is indexed at upsert, from the metadata fields the index designated `full_text` at [creation](../client/create-index). Put the searchable text in those fields and it is analyzed and added to the encrypted BM25 index automatically, making the item retrievable via `text` on [`/v1/vectors/query`](./query) and [`/v1/vectors/query_metadata`](./query-metadata).

```json theme={null}
{
  "id": "article_1",
  "vector": [0.1, 0.2, 0.3, 0.4],
  "metadata": {
    "title": "Encrypted vector search",
    "body": "CyborgDB keeps embeddings encrypted during search...",
    "category": "tech"
  }
}
```

Here `title` and `body` are BM25-searchable if the index marked them `full_text`; `category` (a regular `filterable` field) is exact-match indexed as usual.

* A `full_text` field's value must be a JSON **string**. Items where the field is missing or non-string are skipped for that field — the upsert still succeeds, and the service logs one warning per field per batch.
* `full_text` designations are fixed at index creation; text upserted into non-designated fields is stored as ordinary metadata and is not text-searchable.
* `full_text` fields are analyzed instead of exact-match indexed (`filterable: false`), so they cannot be used in `filters` on [`/v1/vectors/query_metadata`](./query-metadata) or on hybrid queries.

## Response

**Standard Success Response:**

```json theme={null}
{
  "status": "success",
  "message": "Upserted 2 vectors"
}
```

**With Auto-Training Triggered:**

```json theme={null}
{
  "status": "success",
  "message": "Upserted 2 vectors",
  "training_triggered": true,
  "training_message": "Index training has been triggered (vectors: 5000)"
}
```

**With Training Check Warning:**

```json theme={null}
{
  "status": "success",
  "message": "Upserted 2 vectors",
  "warning": "Training check failed but upsert succeeded"
}
```

<Note>
  When the number of vectors in an index exceeds the automatic training threshold, the upsert response will include `training_triggered` and `training_message` fields. Training happens asynchronously in the background.

  In rare cases where the training check encounters an error but the upsert operation itself succeeds, a `warning` field will be included instead. The vectors are still successfully upserted in this scenario.
</Note>

## Exceptions

<AccordionGroup>
  <Accordion title="400 Bad Request">
    Invalid values (e.g. vector dimension mismatch with the index)
  </Accordion>

  <Accordion title="401 Unauthorized">
    Authentication failed (invalid API key) **or** wrong `index_key` on SDK-supplied indexes — see [error model](../introduction#error-model-api-keys-index-keys-and-kms)
  </Accordion>

  <Accordion title="404 Not Found">
    Index not found
  </Accordion>

  <Accordion title="422 Unprocessable Entity">
    Request body failed validation (missing/mistyped fields)
  </Accordion>

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

  <Accordion title="503 Service Unavailable">
    Embedding model unavailable (auto-embedding upserts on indexes with an `embedding_model`)
  </Accordion>
</AccordionGroup>

## Example Usage

**Basic Upsert:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/vectors/upsert" \
     -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",
       "items": [
         {
           "id": "item_1",
           "vector": [0.1, 0.2, 0.3, 0.4],
           "contents": "Hello world!"
         }
       ]
     }'
```

**With Metadata:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/vectors/upsert" \
     -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",
       "items": [
         {
           "id": "doc_1",
           "vector": [0.1, 0.2, 0.3, 0.4],
           "contents": "Important document content",
           "metadata": {
             "category": "documentation",
             "author": "admin",
             "created": "2024-01-15",
             "priority": "high"
           }
         }
       ]
     }'
```

**Auto-Generated Embeddings:**

```bash theme={null}
# For indexes created with embedding_model
curl -X POST "http://localhost:8000/v1/vectors/upsert" \
     -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",
       "items": [
         {
           "id": "text_1",
           "contents": "This text will be automatically embedded",
           "metadata": {"type": "auto_embedded"}
         }
       ]
     }'
```

**Batch Upsert:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/vectors/upsert" \
     -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",
       "items": [
         {"id": "batch_1", "vector": [0.1, 0.2, 0.3]},
         {"id": "batch_2", "vector": [0.4, 0.5, 0.6]},
         {"id": "batch_3", "vector": [0.7, 0.8, 0.9]},
         {"id": "batch_4", "vector": [0.2, 0.3, 0.4]},
         {"id": "batch_5", "vector": [0.5, 0.6, 0.7]}
       ]
     }'
```

<Note>When `embedding_model` is configured for the index, the `vector` parameter becomes optional. If provided, it will be used directly; if omitted, a vector will be auto-generated from the `contents` field.</Note>

<Tip>For large datasets, use batch upserts with multiple items in a single request to improve performance and reduce network overhead.</Tip>


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