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

# Upload & Process

> Get documents into your vault and make them searchable

Three steps to make a document searchable:

1. **Create a vault** (once per case)
2. **Upload your file**
3. **Process it** (we call this "ingest")

After processing, the document is fully searchable by meaning.

***

## Step 1: Create a vault

A vault is a container for your documents. Create one per case or matter.

```bash title="Endpoint" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
POST /vault
```

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  curl -X POST https://api.case.dev/vault \
    -H "Authorization: Bearer $CASEDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Smith v. Hospital 2024",
      "description": "Discovery and depositions"
    }'
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev vault create --name "Smith v. Hospital 2024"
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  import Casedev from 'casedev';

  const client = new Casedev({ apiKey: 'sk_case_YOUR_API_KEY' });

  const vault = await client.vault.create({
    name: 'Smith v. Hospital 2024',
    description: 'Discovery and depositions'
  });

  console.log(vault.id);  // Save this—you'll use it for everything
  ```

  ```python title="Python" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  import casedev

  client = casedev.Casedev(api_key='sk_case_YOUR_API_KEY')

  vault = client.vault.create(
      name='Smith v. Hospital 2024',
      description='Discovery and depositions'
  )

  print(vault.id)  # Save this—you'll use it for everything
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  vault, _ := client.Vault.New(ctx, casedev.VaultNewParams{
  	Name: casedev.F("Smith v. Hospital 2024"),
  	Description: casedev.F("Discovery and depositions"),
  })
  fmt.Println(vault.ID)
  ```
</CodeGroup>

```json title="Response" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
{
  "id": "vault_abc123xyz",
  "name": "Smith v. Hospital 2024",
  "description": "Discovery and depositions",
  "enableIndexing": true,
  "createdAt": "2025-01-15T10:30:00Z"
}
```

### Vault options

| Option           | Default  | Description                                                     |
| ---------------- | -------- | --------------------------------------------------------------- |
| `name`           | required | Display name for the vault                                      |
| `description`    | optional | Description of the vault's purpose                              |
| `groupId`        | optional | Assign the vault to a [group](/vault/groups) for access control |
| `enableIndexing` | `true`   | Enable vector search. Set `false` for storage-only vaults       |
| `enableGraph`    | `true`   | Enable GraphRAG knowledge graph (requires indexing)             |

<Info>
  If your API key is [scoped to specific groups](/vault/groups#scoped-api-keys), `groupId` is required and must be a group the key has access to.
</Info>

### Storage-only vaults

If you only need document storage without search capabilities, create a storage-only vault:

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  curl -X POST https://api.case.dev/vault \
    -H "Authorization: Bearer $CASEDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"name": "Archive Storage", "enableIndexing": false}'
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev vault create --name "Archive Storage"
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  const vault = await client.vault.create({
    name: 'Archive Storage',
    enableIndexing: false  // No vector index, no search
  });
  ```

  ```python title="Python" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  vault = client.vault.create(
      name='Archive Storage',
      enable_indexing=False  # No vector index, no search
  )
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  vault, _ := client.Vault.New(ctx, casedev.VaultNewParams{
  	Name: casedev.F("Archive Storage"),
  	EnableIndexing: casedev.F(false),
  })
  fmt.Println(vault.ID)
  ```
</CodeGroup>

<Info>
  **Storage-only vaults** skip vector bucket and index creation. Files can be uploaded and downloaded, but search and GraphRAG are unavailable. Use this for pure archival or when you'll process documents externally.
</Info>

***

## Step 2: Upload a file

Uploading is two parts: get a secure URL, then PUT your file to it.

```bash title="Endpoint" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
POST /vault/:id/upload
```

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  # 1. Get upload URL
  UPLOAD=$(curl -s -X POST "https://api.case.dev/vault/$VAULT_ID/upload" \
    -H "Authorization: Bearer $CASEDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"filename": "deposition.pdf", "contentType": "application/pdf"}')

  UPLOAD_URL=$(echo $UPLOAD | jq -r '.uploadUrl')
  OBJECT_ID=$(echo $UPLOAD | jq -r '.objectId')

  # 2. Upload the file
  curl -X PUT "$UPLOAD_URL" \
    -H "Content-Type: application/pdf" \
    --data-binary "@deposition.pdf"
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev vault upload \
    --id $VAULT_ID \
    --filename "document.pdf" \
    --content-type "application/pdf"
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  // Get an upload URL
  const upload = await client.vault.upload(vault.id, {
    filename: 'deposition-johnson.pdf',
    contentType: 'application/pdf',
    metadata: {
      witness: 'Dr. Sarah Johnson',
      date: '2024-11-04'
    }
  });

  // Upload the file directly to S3
  await fetch(upload.uploadUrl, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/pdf' },
    body: fileBuffer
  });

  console.log(upload.objectId);  // Use this for the next step
  ```

  ```python title="Python" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  import requests

  # Get an upload URL
  upload = client.vault.upload(vault.id,
      filename='deposition-johnson.pdf',
      content_type='application/pdf',
      metadata={
          'witness': 'Dr. Sarah Johnson',
          'date': '2024-11-04'
      }
  )

  # Upload the file directly to S3
  with open('deposition-johnson.pdf', 'rb') as f:
      requests.put(upload.upload_url, data=f,
          headers={'Content-Type': 'application/pdf'})

  print(upload.object_id)  # Use this for the next step
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  upload, _ := client.Vault.Upload(ctx, vaultID, casedev.VaultUploadParams{
  	Filename:    casedev.F("document.pdf"),
  	ContentType: casedev.F("application/pdf"),
  })
  // PUT file to upload.UploadURL via net/http
  fmt.Println(upload.ObjectID)
  ```
</CodeGroup>

### Why two steps?

Security and speed. Your file goes directly to encrypted storage—we're never a bottleneck for large uploads.

### Supported file types

| Type      | Extensions                                      |
| --------- | ----------------------------------------------- |
| Documents | `.pdf`, `.doc`, `.docx`, `.txt`, `.rtf`, `.xml` |
| Images    | `.png`, `.jpg`, `.jpeg`, `.tiff`, `.bmp`        |
| Audio     | `.mp3`, `.m4a`, `.wav`, `.flac`, `.ogg`         |
| Video     | `.mp4`, `.webm`, `.mov`                         |

***

## Step 3: Process the file

This is where the magic happens. Processing (we call it "ingest") does different things based on file type:

| File type                        | What we do                     |
| -------------------------------- | ------------------------------ |
| Scanned PDF, images              | OCR to extract text            |
| Audio, video                     | Transcribe with speaker labels |
| Digital PDF, DOCX, TXT, RTF, XML | Extract text directly          |

Then for all files: split into chunks, convert to vectors, index for search.

```bash title="Endpoint" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
POST /vault/:vaultId/ingest/:objectId
```

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  curl -X POST "https://api.case.dev/vault/$VAULT_ID/ingest/$OBJECT_ID" \
    -H "Authorization: Bearer $CASEDEV_API_KEY"
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev vault ingest --id $VAULT_ID --object-id $OBJECT_ID
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  const result = await client.vault.ingest(vault.id, upload.objectId);

  console.log(result.status);  // 'processing'
  ```

  ```python title="Python" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  result = client.vault.ingest(upload.object_id, id=vault.id)

  print(result.status)  # 'processing'
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  result, _ := client.Vault.Ingest(ctx, vaultID, objectID)
  fmt.Println(result.Status)
  ```
</CodeGroup>

### Processing time

| File                 | Time            |
| -------------------- | --------------- |
| 10-page clean PDF    | \~10 seconds    |
| 50-page scanned PDF  | \~2-3 minutes   |
| 300-page scanned PDF | \~12-15 minutes |
| 1-hour audio/video   | \~5-10 minutes  |

Processing is async—you get an immediate response while we work in the background.

### Use webhooks (recommended for production)

Polling works, but webhooks scale better and give your users faster updates.

1. Create a vault event subscription
2. Receive lifecycle events as ingestion progresses
3. Update your app state from webhook payloads

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  curl -X POST "https://api.case.dev/vault/$VAULT_ID/events/subscriptions" \
    -H "Authorization: Bearer $CASEDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "callbackUrl": "https://your-app.com/webhooks/case-vault",
      "eventTypes": ["vault.ingest.completed", "vault.ingest.failed"]
    }'
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev vault ingest --id $VAULT_ID --object-id $OBJECT_ID
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  const subscription = await client.vault.events.subscriptions.create(vaultId, {
    callbackUrl: 'https://your-app.com/webhooks/case-vault',
    eventTypes: ['vault.ingest.completed', 'vault.ingest.failed']
  });
  ```

  ```python title="Python" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  subscription = client.vault.events.subscriptions.create(vault_id,
      callback_url='https://your-app.com/webhooks/case-vault',
      event_types=['vault.ingest.completed', 'vault.ingest.failed']
  )
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  result, _ := client.Vault.Ingest(ctx, vaultID, objectID)
  fmt.Println(result.Status)
  ```
</CodeGroup>

<Info>
  Webhook delivery is **at-least-once**. Use the payload `id` as your idempotency key and design handlers to safely process duplicates.
</Info>

Need the full webhook flow (signatures, retries, test endpoint, event catalog)?\
[See Vault Webhooks →](/vault/webhooks)

### Check status (polling fallback)

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  # Poll until status is "completed"
  curl "https://api.case.dev/vault/$VAULT_ID/objects/$OBJECT_ID" \
    -H "Authorization: Bearer $CASEDEV_API_KEY"
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev vault:objects retrieve --id $VAULT_ID --object-id $OBJECT_ID
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  // Poll until complete
  let obj = await client.vault.objects.retrieve(vault.id, objectId);

  while (obj.ingestionStatus === 'processing') {
    await new Promise(r => setTimeout(r, 5000));
    obj = await client.vault.objects.retrieve(vault.id, objectId);
  }

  if (obj.ingestionStatus === 'completed') {
    console.log('Ready to search!');
  }
  ```

  ```python title="Python" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  import time

  # Poll until complete
  obj = client.vault.objects.retrieve(vault.id, object_id)

  while obj.ingestion_status == 'processing':
      time.sleep(5)
      obj = client.vault.objects.retrieve(vault.id, object_id)

  if obj.ingestion_status == 'completed':
      print('Ready to search!')
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  // Poll until complete
  obj, _ := client.Vault.Objects.Get(ctx, vaultID, objectID)
  for obj.IngestionStatus == "processing" {
  	time.Sleep(5 * time.Second)
  	obj, _ = client.Vault.Objects.Get(ctx, vaultID, objectID)
  }
  if obj.IngestionStatus == "completed" {
  	fmt.Println("Ready to search!")
  }
  ```
</CodeGroup>

***

## Complete example

Upload an entire folder of discovery documents:

<CodeGroup>
  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev vault create --name "Matter 2024-1234 Discovery"
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  import Casedev from 'casedev';
  import fs from 'fs';
  import path from 'path';

  const client = new Casedev({ apiKey: process.env.CASEDEV_API_KEY });

  async function uploadDiscovery(folderPath: string) {
    // 1. Create vault
    const vault = await client.vault.create({
      name: 'Matter 2024-1234 Discovery'
    });

    // 2. Upload each file
    const files = fs.readdirSync(folderPath);

    for (const file of files) {
      const filePath = path.join(folderPath, file);
      if (!fs.statSync(filePath).isFile()) continue;

      // Get upload URL
      const upload = await client.vault.upload(vault.id, {
        filename: file,
        contentType: 'application/pdf'
      });

      // Upload to S3
      await fetch(upload.uploadUrl, {
        method: 'PUT',
        body: fs.readFileSync(filePath)
      });

      // Process
      await client.vault.ingest(vault.id, upload.objectId);

      console.log(`✓ ${file}`);
    }

    console.log(`\nVault ready: ${vault.id}`);
    return vault.id;
  }

  uploadDiscovery('./discovery_documents');
  ```

  ```python title="Python" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  import os
  import casedev
  import requests
  from pathlib import Path

  client = casedev.Casedev(api_key=os.environ['CASEDEV_API_KEY'])

  def upload_discovery(folder_path: str) -> str:
      # 1. Create vault
      vault = client.vault.create(name='Matter 2024-1234 Discovery')

      # 2. Upload each file
      for file_path in Path(folder_path).iterdir():
          if not file_path.is_file():
              continue

          # Get upload URL
          upload = client.vault.upload(vault.id,
              filename=file_path.name,
              content_type='application/pdf'
          )

          # Upload to S3
          with open(file_path, 'rb') as f:
              requests.put(upload.upload_url, data=f)

          # Process
          client.vault.ingest(upload.object_id, id=vault.id)

          print(f'✓ {file_path.name}')

      print(f'\nVault ready: {vault.id}')
      return vault.id

  upload_discovery('./discovery_documents')
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  vault, _ := client.Vault.New(ctx, casedev.VaultNewParams{
  	Name: casedev.F("Matter 2024-1234 Discovery"),
  })
  fmt.Println(vault.ID)
  ```
</CodeGroup>

<Info>
  **Metadata matters.** Add metadata like `witness`, `document_type`, or `date` when uploading—you can filter search results by these fields later.
</Info>

## Next: Search your documents

Now that your documents are processed, [learn how to search them →](/vault/search)
