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

# Update Corpus

> Replace every mutable field on a corpus in a single request.

## Overview

Replace all mutable corpus fields in a single request. This is a full replacement operation - any field not provided will be reset to its default value.

<Warning>
  **Use with caution**: Omitted fields reset to defaults. For single-field updates, use `PATCH /api/corpora/{id}/` instead.
</Warning>

<Info>
  **Best for**: Renaming a corpus along with other metadata changes, or when you need to reset all fields to known values.
</Info>

## Authentication

Requires valid JWT token or session authentication. You must own the target corpus.

## Path Parameters

<ParamField path="id" type="UUID" required>
  Corpus identifier to update.

  **Example**: `8d0f0a5d-4b5e-4c09-9db6-0e9d2aa8a9fd`
</ParamField>

## Request Body

<ParamField body="corpora_name" type="string" required>
  Corpus name (will be normalized to lowercase with underscores). Must be unique across your corpora.

  **Validation**: The server automatically converts to lowercase and replaces spaces with underscores.

  **Uniqueness**: Fails with `400 Bad Request` if another of your corpora has the same normalized name.
</ParamField>

<ParamField body="description" type="string">
  Human-readable description of the corpus purpose.

  **Default**: Empty string if omitted.
</ParamField>

<ParamField body="is_published" type="boolean" default="false">
  Public visibility flag. Set to `true` to make the corpus discoverable by other users.

  **Default**: `false` (private) if omitted.
</ParamField>

<ParamField body="index_type" type="string" default="VSI">
  Indexing strategy for the corpus:

  * `VSI` - Vector Store Index (best for semantic search)
  * `SMI` - Summary Index
  * `DSI` - Document Summary Index

  **Warning**: Changing this triggers asynchronous re-indexing of all resources.
</ParamField>

<ParamField body="indexing_status" type="string">
  Current processing state. **Advanced use only** - normally managed automatically by the system.

  Valid values: `PND`, `PRS`, `IND`, `ERR`

  <Warning>
    Manually setting this does NOT trigger indexing jobs. Leave this field alone unless coordinating with backend operators.
  </Warning>
</ParamField>

## Example request

```bash theme={null}
curl -X PUT https://{your-host}/api/corpora/8d0f0a5d-4b5e-4c09-9db6-0e9d2aa8a9fd/ \
  -H "Authorization: Bearer $SOAR_LABS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "corpora_name": "support_playbooks",
    "description": "Updated description",
    "is_published": true,
    "index_type": "VSI",
    "indexing_status": "IND"
  }'
```

## Example response

```json theme={null}
{
  "id": "8d0f0a5d-4b5e-4c09-9db6-0e9d2aa8a9fd",
  "created_at": "2024-09-01T10:05:03.291Z",
  "updated_at": "2024-09-02T07:12:44.914Z",
  "corpora_name": "support_playbooks",
  "description": "Updated description",
  "size_on_disk": 4194304.0,
  "index_location": "qdrant_free_collection",
  "is_published": true,
  "index_type": "VSI",
  "indexing_status": "IND",
  "creator": "eb81c1d5-78fe-4f35-b58e-0ff6a3ad5d12"
}
```

## Response Structure

<ResponseField name="id" type="UUID">
  Unchanged corpus identifier.
</ResponseField>

<ResponseField name="created_at" type="timestamp">
  Original creation timestamp (never changes).
</ResponseField>

<ResponseField name="updated_at" type="timestamp">
  Updated to current time when the request succeeds.
</ResponseField>

<ResponseField name="corpora_name" type="string">
  Normalized corpus name after update.
</ResponseField>

<ResponseField name="description" type="string">
  Updated description (or empty string if omitted in request).
</ResponseField>

<ResponseField name="is_published" type="boolean">
  Updated visibility flag (or `false` if omitted).
</ResponseField>

<ResponseField name="index_type" type="string">
  Updated indexing strategy (or `VSI` if omitted).
</ResponseField>

<ResponseField name="indexing_status" type="string">
  Processing state. May show `PRS` if `index_type` was changed and re-indexing started.
</ResponseField>

<ResponseField name="size_on_disk" type="float">
  Unchanged storage size.
</ResponseField>

<ResponseField name="index_location" type="string">
  Vector database collection identifier (unchanged).
</ResponseField>

<ResponseField name="creator" type="UUID">
  Unchanged corpus owner ID (read-only field).
</ResponseField>

## Best Practices

<AccordionGroup>
  <Accordion title="PUT vs PATCH - When to Use Each" icon="code-compare">
    **Use PUT when:**

    * Renaming a corpus along with other changes
    * You want to explicitly reset fields to defaults
    * You have a complete corpus object to replace

    **Use PATCH when:**

    * Updating a single field (e.g., toggling `is_published`)
    * Making incremental changes without affecting other fields
    * You don't want to risk accidentally resetting fields

    **Example - Wrong approach:**

    ```python theme={null}
    # ❌ BAD: Using PUT to only change description
    # This resets is_published to false!
    requests.put(
        f"{base_url}/api/corpora/{corpus_id}/",
        json={"corpora_name": "my_corpus", "description": "New description"}
    )
    ```

    **Example - Correct approach:**

    ```python theme={null}
    # ✅ GOOD: Use PATCH for single field updates
    requests.patch(
        f"{base_url}/api/corpora/{corpus_id}/",
        json={"description": "New description"}
    )
    ```
  </Accordion>

  <Accordion title="Handling Name Collisions" icon="triangle-exclamation">
    The server enforces name uniqueness across your corpora:

    ```python theme={null}
    try:
        response = requests.put(
            f"{base_url}/api/corpora/{corpus_id}/",
            headers=headers,
            json={
                "corpora_name": "duplicate_name",
                "description": "Test",
                "is_published": False,
                "index_type": "VSI"
            }
        )
        response.raise_for_status()
    except requests.HTTPError as e:
        if e.response.status_code == 400:
            error = e.response.json()
            if "corpora_name" in error:
                print("Name collision - choose a different name")
    ```

    **Tip**: Use `GET /api/check_corpora_name/?corpora_name=new_name` before attempting the update.
  </Accordion>

  <Accordion title="Index Type Migration" icon="arrows-rotate">
    Changing `index_type` triggers background re-indexing:

    ```python theme={null}
    # Update to different index type
    response = requests.put(
        f"{base_url}/api/corpora/{corpus_id}/",
        headers=headers,
        json={
            "corpora_name": "support_playbooks",
            "description": "Switching to summary index",
            "index_type": "SMI",  # Changed from VSI
            "is_published": False
        }
    )

    # Monitor re-indexing progress
    while True:
        corpus = requests.get(
            f"{base_url}/api/corpora/{corpus_id}/",
            headers=headers
        ).json()

        if corpus["indexing_status"] == "IND":
            print("Re-indexing complete")
            break
        elif corpus["indexing_status"] == "ERR":
            print("Re-indexing failed")
            break

        time.sleep(10)
    ```

    **Performance impact**: Large corpora may take minutes to re-index. Plan migrations during low-traffic periods.
  </Accordion>

  <Accordion title="Atomic Updates with Verification" icon="shield-check">
    Ensure updates succeed and verify the result:

    ```python theme={null}
    # Get current state
    original = requests.get(
        f"{base_url}/api/corpora/{corpus_id}/",
        headers=headers
    ).json()

    try:
        # Attempt full update
        updated = requests.put(
            f"{base_url}/api/corpora/{corpus_id}/",
            headers=headers,
            json={
                "corpora_name": "renamed_corpus",
                "description": "Updated description",
                "is_published": True,
                "index_type": original["index_type"]  # Keep same type
            }
        ).json()

        # Verify critical fields
        assert updated["corpora_name"] == "renamed_corpus"
        assert updated["is_published"] == True
        print("Update verified successfully")

    except Exception as e:
        print(f"Update failed: {e}")
        # Original corpus state is unchanged on failure
    ```
  </Accordion>
</AccordionGroup>

<Note>
  **Error Handling**: `400 Bad Request` indicates either a name collision or attempt to modify read-only fields like `creator`, `id`, or `created_at`.
</Note>

## Client examples

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import os
    import requests

    BASE_URL = "https://your-soar-instance.com"
    TOKEN = os.environ["SOAR_LABS_TOKEN"]
    CORPUS_ID = "8d0f0a5d-4b5e-4c09-9db6-0e9d2aa8a9fd"

    payload = {
        "corpora_name": "support_playbooks",
        "description": "Updated description",
        "is_published": True,
        "index_type": "VSI",
    }

    response = requests.put(
        f"{BASE_URL}/api/corpora/{CORPUS_ID}/",
        headers={
            "Authorization": f"Bearer {TOKEN}",
            "Content-Type": "application/json",
        },
        json=payload,
        timeout=30,
    )
    response.raise_for_status()
    updated = response.json()
    ```
  </Tab>

  <Tab title="TypeScript / JavaScript">
    ```ts theme={null}
    const BASE_URL = "https://your-soar-instance.com";
    const token = process.env.SOAR_LABS_TOKEN!;

    async function updateCorpus(corpusId: string) {
      const response = await fetch(`${BASE_URL}/api/corpora/${corpusId}/`, {
        method: "PUT",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        body: JSON.stringify({
          corpora_name: "support_playbooks",
          description: "Updated description",
          is_published: true,
          index_type: "VSI",
        }),
      });

      if (!response.ok) {
        throw new Error(`Update corpus failed: ${response.status}`);
      }

      return response.json();
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;

    var BASE_URL = "https://your-soar-instance.com";
    var token = System.getenv("SOAR_LABS_TOKEN");
    var corpusId = "8d0f0a5d-4b5e-4c09-9db6-0e9d2aa8a9fd";

    var json = "{" +
        "\"corpora_name\":\"support_playbooks\"," +
        "\"description\":\"Updated description\"," +
        "\"is_published\":true," +
        "\"index_type\":\"VSI\"" +
    "}";

    var request = HttpRequest.newBuilder(URI.create(BASE_URL + "/api/corpora/" + corpusId + "/"))
        .header("Authorization", "Bearer " + token)
        .header("Content-Type", "application/json")
        .PUT(HttpRequest.BodyPublishers.ofString(json))
        .build();

    var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());

    if (response.statusCode() >= 400) {
        throw new RuntimeException("Update corpus failed: " + response.statusCode());
    }

    var body = response.body();
    ```
  </Tab>
</Tabs>


## OpenAPI

````yaml PUT /api/corpora/{id}/
openapi: 3.0.3
info:
  title: Soar Labs API - Insider Dev preview
  version: 0.1.0
  description: <b>Soar Labs Advanced RAG platform</b>
servers: []
security: []
paths:
  /api/corpora/{id}/:
    put:
      tags:
        - Corpora
      description: >-
        Corpora creation API using Django Rest Framework.


        This viewset provides CRUD operations for Corpora objects.


        ### Responses


        - **201 Created**: Returned on successful creation of a Corpora.

        - **400 Bad Request**: Returned if the request data is invalid.

        - **401 Unauthorized**: Returned if the user is not authenticated.

        - **403 Forbidden**: Returned if the user is not authorized to perform
        the operation.

        - **404 Not Found**: Returned if the requested Corpora does not exist.


        ### Permissions

        - `IsAuthenticated`: Users must be authenticated to access this
        endpoint.
      operationId: api_corpora_update
      parameters:
        - in: path
          name: id
          schema:
            type: string
            format: uuid
          description: A UUID string identifying this Corpora.
          required: true
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Corpora'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/Corpora'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/Corpora'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Corpora'
          description: ''
      security:
        - jwtHeaderAuth: []
        - jwtCookieAuth: []
        - cookieAuth: []
        - basicAuth: []
components:
  schemas:
    Corpora:
      type: object
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
          description: The date and time the organization was created
        updated_at:
          type: string
          format: date-time
          readOnly: true
          description: Last updated time
        corpora_name:
          type: string
          description: Name of the corpora
          maxLength: 100
        description:
          type: string
          nullable: true
          description: Description of the corpora
        size_on_disk:
          type: number
          format: double
          readOnly: true
          title: Size
          description: Size of the corpora on disk (in bytes)
        index_location:
          type: string
          readOnly: true
          nullable: true
          title: Location of Index
          description: Location of the index on Remote Storage
        is_published:
          type: boolean
          title: Publishing Status
          description: Is the corpora Visible to all users?
        index_type:
          allOf:
            - $ref: '#/components/schemas/IndexTypeEnum'
          description: |-
            Type of index to be used for the corpora

            * `VSI` - VectorStoreIndex
            * `SMI` - SummaryIndex
            * `DSI` - DocumentSummaryIndex
        indexing_status:
          allOf:
            - $ref: '#/components/schemas/IndexingStatusEnum'
          title: Index Processing Status
          description: |-
            Status of the corpora processing

            * `PND` - Pending
            * `IQE` - In Queue
            * `PRS` - Processing
            * `DEX` - Data Extracted Successfully
            * `DER` - Data Extraction Error
            * `IND` - Indexed
            * `CMP` - Completed
            * `ERR` - Error
        creator:
          type: string
          format: uuid
          readOnly: true
      required:
        - corpora_name
        - created_at
        - creator
        - id
        - index_location
        - size_on_disk
        - updated_at
    IndexTypeEnum:
      enum:
        - VSI
        - SMI
        - DSI
      type: string
      description: |-
        * `VSI` - VectorStoreIndex
        * `SMI` - SummaryIndex
        * `DSI` - DocumentSummaryIndex
    IndexingStatusEnum:
      enum:
        - PND
        - IQE
        - PRS
        - DEX
        - DER
        - IND
        - CMP
        - ERR
      type: string
      description: |-
        * `PND` - Pending
        * `IQE` - In Queue
        * `PRS` - Processing
        * `DEX` - Data Extracted Successfully
        * `DER` - Data Extraction Error
        * `IND` - Indexed
        * `CMP` - Completed
        * `ERR` - Error
  securitySchemes:
    jwtHeaderAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    jwtCookieAuth:
      type: apiKey
      in: cookie
      name: soar-app-auth
    cookieAuth:
      type: apiKey
      in: cookie
      name: sessionid
    basicAuth:
      type: http
      scheme: basic

````