1. Create a vault
Vaults are secure containers for your users’ documents. Each vault gets automatic OCR, chunking, and vector indexing.curl -X POST https://api.case.dev/vault \
-H "Authorization: Bearer $CASEDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Case Documents - User 12345",
"description": "Discovery documents for case review"
}'
casedev vault create --name "Case Documents - User 12345"
import Casedev from 'casedev';
const client = new Casedev({ apiKey: process.env.CASEDEV_API_KEY });
// Create a vault for your user's documents
const vault = await client.vault.create({
name: 'Case Documents - User 12345',
description: 'Discovery documents for case review'
});
console.log(`Vault created: ${vault.id}`);
import casedev
import os
client = casedev.Casedev(api_key=os.environ['CASEDEV_API_KEY'])
# Create a vault for your user's documents
vault = client.vault.create(
name='Case Documents - User 12345',
description='Discovery documents for case review'
)
print(f'Vault created: {vault.id}')
vault, _ := client.Vault.New(ctx, casedev.VaultNewParams{
Name: casedev.F("Case Documents - User 12345"),
Description: casedev.F("Discovery documents for case review"),
})
fmt.Println(vault.ID)
2. Upload documents
Handle file uploads from your users and trigger automatic processing:# 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": "document.pdf", "contentType": "application/pdf"}')
UPLOAD_URL=$(echo $UPLOAD | jq -r '.uploadUrl')
OBJECT_ID=$(echo $UPLOAD | jq -r '.objectId')
# 2. Upload file
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/pdf" \
--data-binary "@document.pdf"
# 3. Trigger ingestion
curl -X POST "https://api.case.dev/vault/$VAULT_ID/ingest/$OBJECT_ID" \
-H "Authorization: Bearer $CASEDEV_API_KEY"
casedev vault upload \
--id $VAULT_ID \
--filename "document.pdf" \
--content-type "application/pdf"
import fs from 'fs';
async function uploadDocument(vaultId: string, filePath: string) {
// Get presigned upload URL
const upload = await client.vault.upload(vaultId, {
filename: filePath.split('/').pop()!,
contentType: 'application/pdf'
});
// Upload file to S3
const file = fs.readFileSync(filePath);
await fetch(upload.uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': 'application/pdf' },
body: file
});
// Trigger OCR + embedding pipeline
await client.vault.ingest(vaultId, upload.objectId);
return upload.objectId;
}
// Process uploads from your user
const files = fs.readdirSync('./uploads');
for (const file of files) {
await uploadDocument(vault.id, `./uploads/${file}`);
console.log(`Processed: ${file}`);
}
import os
import requests
def upload_document(vault_id: str, file_path: str) -> str:
# Get presigned upload URL
upload = client.vault.upload(vault_id,
filename=os.path.basename(file_path),
content_type='application/pdf'
)
# Upload file to S3
with open(file_path, 'rb') as f:
requests.put(upload.upload_url, data=f,
headers={'Content-Type': 'application/pdf'})
# Trigger OCR + embedding pipeline
client.vault.ingest(upload.object_id, id=vault_id)
return upload.object_id
# Process uploads from your user
for file in os.listdir('./uploads'):
upload_document(vault.id, f'./uploads/{file}')
print(f'Processed: {file}')
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)
3. Search by meaning
Enable your users to search by meaning, not just keywords:curl -X POST "https://api.case.dev/vault/$VAULT_ID/search" \
-H "Authorization: Bearer $CASEDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "communications about equipment failure",
"method": "hybrid",
"topK": 10
}'
casedev vault search \
--id $VAULT_ID \
--query "search query"
// In your search endpoint or UI handler
const results = await client.vault.search(vault.id, {
query: userQuery, // e.g., "communications about equipment failure"
method: 'hybrid',
topK: 10
});
// Return results to your user
for (const chunk of results.chunks) {
console.log(`📄 ${chunk.filename} (score: ${chunk.hybridScore.toFixed(2)})`);
console.log(`"${chunk.text.substring(0, 200)}..."`);
}
# In your search endpoint or UI handler
results = client.vault.search(vault.id,
query=user_query, # e.g., "communications about equipment failure"
method='hybrid',
top_k=10
)
# Return results to your user
for chunk in results.chunks:
print(f'📄 {chunk.filename} (score: {chunk.hybrid_score:.2f})')
print(f'"{chunk.text[:200]}..."')
results, _ := client.Vault.Search(ctx, vaultID, casedev.VaultSearchParams{
Query: casedev.F("search query"),
Method: casedev.F(casedev.VaultSearchParamsMethodHybrid),
})
for _, chunk := range results.Chunks {
fmt.Println(chunk.Text)
}
4. Summarize findings
Enhance results with AI-generated summaries for your users:curl -X POST https://api.case.dev/llm/v1/chat/completions \
-H "Authorization: Bearer $CASEDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-4.5",
"messages": [
{"role": "system", "content": "Summarize these search results concisely."},
{"role": "user", "content": "User searched for: [QUERY]\n\nResults:\n\n[SEARCH RESULTS]"}
],
"max_tokens": 500
}'
casedev llm:v1:chat create-completion \
--model anthropic/claude-sonnet-4.5 \
--message '{role: system, content: "Summarize these search results concisely. Highlight the most relevant findings."}' \
--message '{role: user, content: "User searched for: <query>\n\nResults:\n\n<search results>"}' \
--max-tokens 500
const context = results.chunks.map(c => c.text).join('\n\n---\n\n');
const summary = await client.llm.v1.chat.createCompletion({
model: 'anthropic/claude-sonnet-4.5',
messages: [
{
role: 'system',
content: 'Summarize these search results concisely. Highlight the most relevant findings.'
},
{
role: 'user',
content: `User searched for: "${userQuery}"\n\nResults:\n\n${context}`
}
],
max_tokens: 500
});
// Return summary along with search results
console.log(summary.choices[0].message.content);
context = '\n\n---\n\n'.join([c.text for c in results.chunks])
summary = client.llm.v1.chat.create_completion(
model='anthropic/claude-sonnet-4.5',
messages=[
{
'role': 'system',
'content': 'Summarize these search results concisely. Highlight the most relevant findings.'
},
{
'role': 'user',
'content': f'User searched for: "{user_query}"\n\nResults:\n\n{context}'
}
],
max_tokens=500
)
# Return summary along with search results
print(summary.choices[0].message.content)
// Summarize search results for your user
resp, _ := client.Llm.V1.Chat.NewCompletion(ctx, casedev.LlmV1ChatNewCompletionParams{
Model: casedev.F("anthropic/claude-sonnet-4.5"),
Messages: casedev.F([]casedev.LlmV1ChatNewCompletionParamsMessage{
{
Role: casedev.F(casedev.LlmV1ChatNewCompletionParamsMessagesRoleSystem),
Content: casedev.F("Summarize these search results concisely. Highlight the most relevant findings."),
},
{
Role: casedev.F(casedev.LlmV1ChatNewCompletionParamsMessagesRoleUser),
Content: casedev.F("User searched for: " + query + "\n\nResults:\n\n" + searchResultsText),
},
}),
MaxTokens: casedev.F(int64(500)),
})
fmt.Println(resp.Choices[0].Message.Content)
Time saved: What used to take weeks of manual review now takes minutes. The AI finds relevant passages even when documents use different terminology.

