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

# Digitize Evidence

> Extract text from scanned documents

**The problem:** Opposing counsel sent you 500 pages of blurry photocopies. You need to search them, but they're just images.

**The solution:** Run OCR to extract text, then search or analyze with AI.

## 1. Submit for OCR

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  curl -X POST https://api.case.dev/ocr/v1/process \
    -H "Authorization: Bearer $CASEDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "document_url": "https://your-storage.com/user-upload.pdf",
      "engine": "doctr",
      "features": {"embed": {}}
    }'
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev ocr:v1 process \
    --document-url "https://storage.example.com/document.pdf"
  ```

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

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

  // Process a document uploaded by your user
  const job = await client.ocr.v1.process({
    document_url: documentUrl, // URL from your user's upload
    engine: 'doctr',  // Fast, good for printed text
    features: {
      embed: {}  // Generate searchable PDF
    }
  });

  console.log(`OCR job started: ${job.id}`);
  ```

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

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

  # Process a document uploaded by your user
  job = client.ocr.v1.process(
      document_url=document_url,  # URL from your user's upload
      engine='doctr',  # Fast, good for printed text
      features={
          'embed': {}  # Generate searchable PDF
      }
  )

  print(f'OCR job started: {job.id}')
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  job, _ := client.Ocr.V1.Process(ctx, casedev.OcrV1ProcessParams{
  	DocumentURL: casedev.F("https://storage.example.com/document.pdf"),
  })
  fmt.Println(job.ID)
  ```
</CodeGroup>

## 2. Wait for completion

OCR runs asynchronously. Poll for status or use webhooks to notify your users:

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  # Poll for status
  curl "https://api.case.dev/ocr/v1/$JOB_ID" \
    -H "Authorization: Bearer $CASEDEV_API_KEY"
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev ocr:v1 retrieve --id $JOB_ID
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  // Poll for completion
  let result = await client.ocr.v1.retrieve(job.id);

  while (result.status === 'processing' || result.status === 'pending') {
    console.log(`Status: ${result.status} (${result.chunks_completed}/${result.chunk_count} pages)`);
    await new Promise(r => setTimeout(r, 5000));
    result = await client.ocr.v1.retrieve(job.id);
  }

  if (result.status === 'completed') {
    console.log(`✅ OCR complete! ${result.page_count} pages processed.`);
    console.log(`Confidence: ${(result.confidence * 100).toFixed(1)}%`);
  }
  ```

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

  # Poll for completion
  result = client.ocr.v1.retrieve(job.id)

  while result.status in ['processing', 'pending']:
      print(f'Status: {result.status} ({result.chunks_completed}/{result.chunk_count} pages)')
      time.sleep(5)
      result = client.ocr.v1.retrieve(job.id)

  if result.status == 'completed':
      print(f'✅ OCR complete! {result.page_count} pages processed.')
      print(f'Confidence: {result.confidence * 100:.1f}%')
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  result, _ := client.Ocr.V1.Get(ctx, jobID)
  fmt.Println(result.Status)
  ```
</CodeGroup>

## 3. Download results

Provide extracted text, structured data, or a searchable PDF:

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  # Download text
  curl "https://api.case.dev/ocr/v1/$JOB_ID/download/text" \
    -H "Authorization: Bearer $CASEDEV_API_KEY" \
    -o extracted.txt

  # Download searchable PDF
  curl "https://api.case.dev/ocr/v1/$JOB_ID/download/pdf" \
    -H "Authorization: Bearer $CASEDEV_API_KEY" \
    -o searchable.pdf
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev ocr:v1 download --id $JOB_ID --type text
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  // Download plain text for your user
  const text = await client.ocr.v1.download(job.id, 'text');

  // Download searchable PDF (original with invisible text layer)
  const pdf = await client.ocr.v1.download(job.id, 'pdf');
  fs.writeFileSync('searchable-document.pdf', Buffer.from(pdf));

  // Download structured JSON (with word coordinates for highlighting)
  const json = await client.ocr.v1.download(job.id, 'json');
  console.log(`Extracted ${json.pages.length} pages`);
  ```

  ```python title="Python" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  # Download plain text for your user
  text = client.ocr.v1.download(job.id, 'text')

  # Download searchable PDF (original with invisible text layer)
  pdf = client.ocr.v1.download(job.id, 'pdf')
  with open('searchable-document.pdf', 'wb') as f:
      f.write(pdf)

  # Download structured JSON (with word coordinates for highlighting)
  json_data = client.ocr.v1.download(job.id, 'json')
  print(f'Extracted {len(json_data["pages"])} pages')
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  text, _ := client.Ocr.V1.Download(ctx, jobID, casedev.OcrV1DownloadParamsTypeText)
  // text is *http.Response with content body
  ```
</CodeGroup>

## 4. Analyze with AI

Enhance your feature with automatic data extraction:

<CodeGroup>
  ```bash title="cURL" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  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": "Extract key dates, parties, and claims. Format as JSON."},
        {"role": "user", "content": "[OCR TEXT]"}
      ],
      "temperature": 0
    }'
  ```

  ```bash title="CLI" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  casedev llm:v1:chat create-completion \
    --model anthropic/claude-sonnet-4.5 \
    --message '{role: system, content: "Extract key dates, parties, and claims from this document. Format as JSON."}' \
    --message '{role: user, content: "<OCR text>"}' \
    --temperature 0
  ```

  ```typescript title="Typescript" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  // Extract key information for your user
  const analysis = await client.llm.v1.chat.createCompletion({
    model: 'anthropic/claude-sonnet-4.5',
    messages: [
      {
        role: 'system',
        content: 'Extract key dates, parties, and claims from this document. Format as JSON.'
      },
      {
        role: 'user',
        content: text
      }
    ],
    temperature: 0  // Deterministic for factual extraction
  });

  // Return structured data to your user
  console.log(analysis.choices[0].message.content);
  ```

  ```python title="Python" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  # Extract key information for your user
  analysis = client.llm.v1.chat.create_completion(
      model='anthropic/claude-sonnet-4.5',
      messages=[
          {
              'role': 'system',
              'content': 'Extract key dates, parties, and claims from this document. Format as JSON.'
          },
          {
              'role': 'user',
              'content': text
          }
      ],
      temperature=0  # Deterministic for factual extraction
  )

  # Return structured data to your user
  print(analysis.choices[0].message.content)
  ```

  ```go title="Go" theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  // Extract key information 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("Extract key dates, parties, and claims from this document. Format as JSON."),
  		},
  		{
  			Role:    casedev.F(casedev.LlmV1ChatNewCompletionParamsMessagesRoleUser),
  			Content: casedev.F(text), // OCR-extracted text
  		},
  	}),
  	Temperature: casedev.F(float64(0)),
  })
  fmt.Println(resp.Choices[0].Message.Content)
  ```
</CodeGroup>

## OCR engines

Choose the right engine based on your users' document types:

| Engine      | Best for                       | Speed  |
| ----------- | ------------------------------ | ------ |
| `doctr`     | Clean printed text             | Fast   |
| `paddleocr` | Tables, forms, complex layouts | Slower |

<Info>
  **Recommendation:** Start with `doctr` for most use cases. Switch to `paddleocr` if your users need table extraction or have complex document layouts.
</Info>
