Skip to main content

What You’ll Build

A workflow that:
  1. Accepts a vault ID and search query via webhook
  2. Searches your indexed documents
  3. Analyzes the results with GPT-4o
  4. Returns structured insights
Time to complete: 15 minutes

Architecture

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   Webhook    │ ──▶ │ Vault Search │ ──▶ │   LLM        │
│   Trigger    │     │   (Hybrid)   │     │   Analysis   │
│              │     │              │     │              │
│  vaultId     │     │  10 chunks   │     │  Structured  │
│  query       │     │  + sources   │     │  response    │
└──────────────┘     └──────────────┘     └──────────────┘

Prerequisites

  • Case.dev API key (get one here)
  • A vault with ingested documents (we’ll set one up if you don’t have one)

Step 1: Set Up Your Vault

If you already have a vault with indexed documents, skip to Step 2.

Create a vault

cURL
export CASEDEV_API_KEY="sk_case_YOUR_API_KEY"

curl -X POST https://api.case.dev/vault \
  -H "Authorization: Bearer $CASEDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Legal Research Vault"}'
Save the returned id — you’ll need it.

Upload a document

cURL
# Get upload URL
UPLOAD_RESPONSE=$(curl -s -X POST https://api.case.dev/vault/YOUR_VAULT_ID/upload \
  -H "Authorization: Bearer $CASEDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "contract.pdf",
    "contentType": "application/pdf"
  }')

# Extract the upload URL and object ID
UPLOAD_URL=$(echo $UPLOAD_RESPONSE | jq -r '.uploadUrl')
OBJECT_ID=$(echo $UPLOAD_RESPONSE | jq -r '.objectId')

# Upload your file
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  --data-binary @contract.pdf

echo "Object ID: $OBJECT_ID"

Ingest (index) the document

cURL
curl -X POST https://api.case.dev/vault/YOUR_VAULT_ID/ingest/$OBJECT_ID \
  -H "Authorization: Bearer $CASEDEV_API_KEY"
Ingestion is async. Wait for ingestionStatus: "completed" before searching. Poll the object endpoint to check status.
cURL
# Check status (poll until completed)
curl https://api.case.dev/vault/YOUR_VAULT_ID/objects/$OBJECT_ID \
  -H "Authorization: Bearer $CASEDEV_API_KEY" | jq '.ingestionStatus'

Step 2: Create the Workflow

Now let’s create the document analyzer workflow. Save this as document-analyzer.json:
JSON
{
  "name": "Document Analyzer",
  "description": "Search vault documents and generate AI analysis",
  "nodes": [
    {
      "id": "trigger-1",
      "type": "trigger",
      "label": "Webhook Trigger",
      "config": {
        "triggerType": "Webhook"
      }
    },
    {
      "id": "search-1",
      "type": "action",
      "label": "Vault Search",
      "config": {
        "actionType": "case-search/vault-search",
        "vaultId": "{{vaultId}}",
        "query": "{{query}}",
        "limit": 10,
        "searchMethod": "hybrid"
      }
    },
    {
      "id": "analyze-1",
      "type": "action",
      "label": "Analyze",
      "config": {
        "actionType": "case-llm/llm",
        "method": "chat",
        "model": "gpt-4o",
        "systemPrompt": "You are a legal document analyst. Analyze the provided document excerpts and answer the user's question. Always cite specific passages from the documents to support your analysis. If the documents don't contain relevant information, say so clearly.",
        "userPrompt": "## Document Excerpts\n\n{{results.Vault_Search.output.chunks}}\n\n## Sources\n{{results.Vault_Search.output.sources}}\n\n## Question\n{{query}}"
      }
    }
  ],
  "edges": [
    { "source": "trigger-1", "target": "search-1" },
    { "source": "search-1", "target": "analyze-1" }
  ]
}

Deploy the workflow

cURL
curl -X POST https://api.case.dev/workflows/v1/create \
  -H "Authorization: Bearer $CASEDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d @document-analyzer.json
Response:
Response
{
  "id": "wf_abc123xyz",
  "name": "Document Analyzer",
  "webhookUrl": "https://api.case.dev/workflows/v1/wf_abc123xyz/webhook",
  "webhookSecret": "whsec_aBcDeFgHiJkLmNoP",
  "status": "deployed",
  "message": "Workflow created and deployed successfully."
}
Save your webhook secret! It’s only shown once. You’ll need it to trigger executions.

Step 3: Execute the Workflow

Now trigger your workflow with a vault ID and query:
cURL
curl -X POST "https://api.case.dev/workflows/v1/wf_abc123xyz/webhook" \
  -H "X-Webhook-Secret: whsec_aBcDeFgHiJkLmNoP" \
  -H "Content-Type: application/json" \
  -d '{
    "vaultId": "YOUR_VAULT_ID",
    "query": "What are the key terms of this agreement?"
  }'

Async execution (default)

The default response returns an execution ID immediately:
Response
{
  "executionId": "wfex_execution123",
  "status": "running",
  "mode": "fire-and-forget"
}
Check status:
cURL
curl https://api.case.dev/workflows/v1/runs/wfex_execution123 \
  -H "Authorization: Bearer $CASEDEV_API_KEY"

Synchronous execution

For immediate results, add ?mode=sync:
cURL
curl -X POST "https://api.case.dev/workflows/v1/wf_abc123xyz/webhook?mode=sync" \
  -H "X-Webhook-Secret: whsec_aBcDeFgHiJkLmNoP" \
  -H "Content-Type: application/json" \
  -d '{
    "vaultId": "YOUR_VAULT_ID",
    "query": "What are the key terms of this agreement?"
  }'
Response includes the full analysis:
Response
{
  "status": "Completed",
  "duration": "8.42s",
  "output": {
    "results": {
      "Vault_Search": {
        "output": {
          "chunks": [...],
          "sources": [...]
        }
      },
      "Analyze": {
        "output": {
          "choices": [{
            "message": {
              "content": "Based on the contract excerpts, the key terms include:\n\n1. **Payment Terms**: Section 3.2 states that payment is due within 30 days...\n\n2. **Termination**: Either party may terminate with 90 days written notice (Section 7.1)...\n\n3. **Liability Cap**: Liability is limited to the total fees paid in the preceding 12 months (Section 9.3)..."
            }
          }]
        }
      }
    }
  }
}

Step 4: Access the Analysis

The LLM response is at:
output.results.Analyze.output.choices[0].message.content
Example extraction with jq:
cURL
curl -s https://api.case.dev/workflows/v1/runs/wfex_execution123 \
  -H "Authorization: Bearer $CASEDEV_API_KEY" \
  | jq -r '.output.results.Analyze.output.choices[0].message.content'

Extending the Workflow

Add PDF Report Generation

Generate a formatted PDF report from the analysis:
JSON
{
  "id": "format-1",
  "type": "action",
  "label": "Generate PDF",
  "config": {
    "actionType": "case-format/format",
    "content": "# Document Analysis Report\n\n**Query:** {{query}}\n\n## Analysis\n\n{{results.Analyze.output.choices[0].message.content}}\n\n## Sources\n\n{{results.Vault_Search.output.sources}}",
    "inputFormat": "md",
    "outputFormat": "pdf"
  }
}
Add edge: { "source": "analyze-1", "target": "format-1" }

Add Entity Extraction

Extract named entities before analysis:
JSON
{
  "id": "entities-1",
  "type": "action",
  "label": "Extract Entities",
  "config": {
    "actionType": "case-llm/llm",
    "method": "chat",
    "model": "gpt-4o",
    "systemPrompt": "Extract all named entities from the text. Return as JSON with arrays for: PERSON, ORGANIZATION, DATE, LOCATION, MONEY.",
    "userPrompt": "{{results.Vault_Search.output.chunks}}"
  }
}

Add Web Research

Supplement vault search with live web research:
JSON
{
  "id": "web-1",
  "type": "action",
  "label": "Web Research",
  "config": {
    "actionType": "case-search/web-search",
    "query": "{{query}} legal precedent case law",
    "numResults": 5
  }
}

Complete Enhanced Workflow

Here’s the full enhanced workflow with all extensions:
JSON
{
  "name": "Enhanced Document Analyzer",
  "description": "Search, extract entities, research, analyze, and generate PDF",
  "nodes": [
    {
      "id": "trigger",
      "type": "trigger",
      "label": "Webhook",
      "config": { "triggerType": "Webhook" }
    },
    {
      "id": "vault-search",
      "type": "action",
      "label": "Vault Search",
      "config": {
        "actionType": "case-search/vault-search",
        "vaultId": "{{vaultId}}",
        "query": "{{query}}",
        "limit": 15,
        "searchMethod": "hybrid"
      }
    },
    {
      "id": "web-search",
      "type": "action",
      "label": "Web Research",
      "config": {
        "actionType": "case-search/web-search",
        "query": "{{query}} legal analysis",
        "numResults": 5
      }
    },
    {
      "id": "extract-entities",
      "type": "action",
      "label": "Extract Entities",
      "config": {
        "actionType": "case-llm/llm",
        "method": "chat",
        "model": "gpt-4o",
        "systemPrompt": "Extract named entities as JSON: {people: [], organizations: [], dates: [], locations: [], monetary_amounts: []}",
        "userPrompt": "{{results.Vault_Search.output.chunks}}"
      }
    },
    {
      "id": "analyze",
      "type": "action",
      "label": "Comprehensive Analysis",
      "config": {
        "actionType": "case-llm/llm",
        "method": "chat",
        "model": "gpt-4o",
        "systemPrompt": "You are a senior legal analyst. Provide comprehensive analysis combining document excerpts with external research. Structure your response with: Executive Summary, Key Findings, Supporting Evidence, and Recommendations.",
        "userPrompt": "## Document Excerpts\n{{results.Vault_Search.output.chunks}}\n\n## Entities Found\n{{results.Extract_Entities.output.choices[0].message.content}}\n\n## External Research\n{{results.Web_Research.output.results}}\n\n## Question\n{{query}}"
      }
    },
    {
      "id": "generate-pdf",
      "type": "action",
      "label": "Generate Report",
      "config": {
        "actionType": "case-format/format",
        "content": "# Legal Analysis Report\n\n**Generated:** {{timestamp}}\n**Query:** {{query}}\n\n---\n\n{{results.Comprehensive_Analysis.output.choices[0].message.content}}\n\n---\n\n## Document Sources\n{{results.Vault_Search.output.sources}}",
        "inputFormat": "md",
        "outputFormat": "pdf"
      }
    }
  ],
  "edges": [
    { "source": "trigger", "target": "vault-search" },
    { "source": "trigger", "target": "web-search" },
    { "source": "vault-search", "target": "extract-entities" },
    { "source": "vault-search", "target": "analyze" },
    { "source": "web-search", "target": "analyze" },
    { "source": "extract-entities", "target": "analyze" },
    { "source": "analyze", "target": "generate-pdf" }
  ]
}

Production Tips

Error Handling

Check execution status before accessing results:
JavaScript
const run = await fetch(`/workflows/v1/runs/${executionId}`);
const data = await run.json();

if (data.status === 'Failed') {
  console.error('Workflow failed:', data.error);
  // Check data.steps for which step failed
}

Rate Limits

  • Vault search: 100 requests/minute
  • LLM calls: Based on your plan
  • Workflow executions: 1000/hour (default)

Cost Optimization

ComponentCost DriverOptimization
Vault SearchPer queryCache common queries
LLM AnalysisToken countUse smaller limit values
PDF GenerationPer pageSummarize before formatting

Monitoring

View all executions in the Visual Builder:
  • Execution timeline
  • Step-by-step inputs/outputs
  • Error details with stack traces

Next Steps