> For the complete documentation index, see [llms.txt](https://integrate.lexamica.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://integrate.lexamica.com/example-integrations/2a.-receive-cases-webhook.md).

# Receive Cases via Webhook

Get notified instantly when cases are assigned to your firm.

***

## 🎯 Overview

> **📌 TL;DR**
>
> Set up a webhook to receive automatic notifications when cases are sent to your firm. Handle everything else (accepting, declining, updates) in the Lexamica platform.

**This guide is for you if:**

* You receive referrals from other firms via Lexamica
* You want to be notified immediately when new cases arrive
* You'll handle invitations and updates through the Lexamica UI

**What you'll build:**

* A Case mapping to receive data in your format
* A webhook subscription for the `Case Received` event
* A webhook handler that stores incoming cases

**What this does NOT cover:**

* Accepting/declining invitations via API
* Sending status updates via API
* Sending cases to other firms

For full API-based handling, see [Handler Firm: Full Integration with Webhooks](/example-integrations/4a.-handler-firm-webhook.md)

***

## 📖 Key Terms

| Term                     | Definition                                                       |
| ------------------------ | ---------------------------------------------------------------- |
| **Webhook Subscription** | Configuration that tells Lexamica to POST events to your URL     |
| **Case Received**        | Event triggered when your firm receives a case from another firm |
| **Signature**            | HMAC-SHA256 hash to verify webhooks are from Lexamica            |

***

## ⚙️ Architecture

```
Receive Cases via Webhook
─────────────────────────

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│    Lexamica     │────▶│  Your Webhook   │────▶│  Your System    │
│                 │     │    Handler      │     │                 │
│ Case sent to    │     │                 │     │ Store case      │
│ your firm       │     │ 1. Verify sig   │     │ data locally    │
│                 │     │ 2. Return 200   │     │                 │
│ Event: "Case    │     │ 3. Process      │     │ Notify staff,   │
│ Received"       │     │    async        │     │ create record   │
└─────────────────┘     └─────────────────┘     └─────────────────┘
```

> **🎯 Key Takeaway**
>
> Lexamica pushes case data to you the moment it arrives. You don't need to poll or check manually.

***

## 📋 Prerequisites

**Credentials:**

* [ ] Organization ID, Public Key, and Private Key from Lexamica

**Infrastructure:**

* [ ] A publicly accessible HTTPS endpoint

**Foundational Docs:**

* [ ] [Organizations and Authentication](/1.-organizations-and-authentication.md) — understand your API keys
* [ ] [Mapping Engine](/2.-mapping-engine.md) — how field transformations work
* [ ] [Webhook Subscriptions](/4.-webhook-subscriptions.md) — receiving events from Lexamica

***

## 💡 Step-by-Step Implementation

### Step 1: Create Your Case Mapping

Create a mapping that transforms Lexamica's Case fields to your format.

**Request:**

```bash
curl -X POST \
  "https://integration.lexamica.com/organization/{orgId}/mapping/create" \
  -H "Authorization: Bearer your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Inbound Case Mapping",
    "modelName": "Case",
    "fieldMappings": [
      {
        "lexamicaField": "_id",
        "foreignField": "lexamica_case_id",
        "lexamicaFieldType": "String",
        "foreignFieldType": "String"
      },
      {
        "lexamicaField": "client.firstName",
        "foreignField": "client_first_name",
        "lexamicaFieldType": "String",
        "foreignFieldType": "String"
      },
      {
        "lexamicaField": "client.lastName",
        "foreignField": "client_last_name",
        "lexamicaFieldType": "String",
        "foreignFieldType": "String"
      },
      {
        "lexamicaField": "client.email",
        "foreignField": "client_email",
        "lexamicaFieldType": "String",
        "foreignFieldType": "String"
      },
      {
        "lexamicaField": "client.phoneNumber",
        "foreignField": "client_phone",
        "lexamicaFieldType": "String",
        "foreignFieldType": "String"
      },
      {
        "lexamicaField": "caseType",
        "foreignField": "practice_area",
        "lexamicaFieldType": "String",
        "foreignFieldType": "String"
      },
      {
        "lexamicaField": "incident.date",
        "foreignField": "incident_date",
        "lexamicaFieldType": "Date",
        "foreignFieldType": "String"
      },
      {
        "lexamicaField": "incident.synopsis",
        "foreignField": "case_description",
        "lexamicaFieldType": "String",
        "foreignFieldType": "String"
      }
    ]
  }'
```

**Response:**

```json
{
  "_id": "64f1a2b3c4d5e6f7a8b9c0d2",
  "label": "Inbound Case Mapping",
  "modelName": "Case",
  "created": "2026-01-15T10:30:00.000Z"
}
```

### Step 2: Create a Webhook Subscription

Subscribe to the `Case Received` event.

**Request:**

```bash
curl -X POST \
  "https://integration.lexamica.com/organization/{orgId}/webhook-subscriptions/create" \
  -H "Authorization: Bearer your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "Case Received",
    "url": "https://your-system.com/webhooks/lexamica/case-received",
    "secret": "your_webhook_secret_key_here",
    "mapping": "64f1a2b3c4d5e6f7a8b9c0d2",
    "description": "Receive new case notifications",
    "active": true
  }'
```

> **⚠️ Warning:** Keep your `secret` secure. You'll use it to verify that webhooks are actually from Lexamica.

### Step 3: Build Your Webhook Handler

Create an endpoint that receives and processes the webhook.

**What your endpoint receives:**

Headers:

```
Content-Type: application/json
X-Lexamica-Signature: sha256=abc123...
X-Lexamica-Event: Case Received
```

Body (in your field format):

```json
{
  "lexamica_case_id": "64f1a2b3c4d5e6f7a8b9c0d3",
  "client_first_name": "Jane",
  "client_last_name": "Smith",
  "client_email": "jane@example.com",
  "client_phone": "555-123-4567",
  "practice_area": "Personal Injury",
  "incident_date": "2026-01-10",
  "case_description": "Rear-end collision on Highway 101."
}
```

***

## 🔧 Complete Code Example

### Node.js/Express Webhook Handler

```javascript
const express = require('express');
const crypto = require('crypto');

const app = express();

// Important: Use raw body for signature verification
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf;
  }
}));

// Configuration
const WEBHOOK_SECRET = process.env.LEXAMICA_WEBHOOK_SECRET;

// Verify webhook signature
function verifySignature(rawBody, signature) {
  if (!signature) return false;
  
  const expected = 'sha256=' + crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(rawBody)
    .digest('hex');
  
  try {
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expected)
    );
  } catch {
    return false;
  }
}

// Webhook endpoint
app.post('/webhooks/lexamica/case-received', async (req, res) => {
  const signature = req.headers['x-lexamica-signature'];
  const eventType = req.headers['x-lexamica-event'];
  
  // 1. Verify signature
  if (!verifySignature(req.rawBody, signature)) {
    console.error('Invalid webhook signature');
    return res.status(401).send('Invalid signature');
  }
  
  // 2. Respond immediately (important!)
  res.status(200).send('OK');
  
  // 3. Process asynchronously
  try {
    await handleCaseReceived(req.body);
  } catch (error) {
    console.error('Error processing webhook:', error);
    // Log for manual review, but don't fail the webhook
  }
});

async function handleCaseReceived(caseData) {
  console.log('New case received:', caseData.lexamica_case_id);
  
  // Store in your database
  await saveCase({
    lexamicaId: caseData.lexamica_case_id,
    clientName: `${caseData.client_first_name} ${caseData.client_last_name}`,
    clientEmail: caseData.client_email,
    clientPhone: caseData.client_phone,
    practiceArea: caseData.practice_area,
    incidentDate: caseData.incident_date,
    description: caseData.case_description,
    receivedAt: new Date()
  });
  
  // Send notification to staff
  await notifyStaff({
    subject: `New Case: ${caseData.client_first_name} ${caseData.client_last_name}`,
    body: `Practice Area: ${caseData.practice_area}\nPhone: ${caseData.client_phone}`
  });
}

// Placeholder functions - implement based on your system
async function saveCase(data) {
  console.log('Saving case:', data);
  // Your database logic here
}

async function notifyStaff(notification) {
  console.log('Notifying staff:', notification);
  // Email/Slack/SMS logic here
}

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});
```

### Python/Flask Webhook Handler

```python
from flask import Flask, request, jsonify
import hmac
import hashlib
import os

app = Flask(__name__)

WEBHOOK_SECRET = os.environ.get('LEXAMICA_WEBHOOK_SECRET')

def verify_signature(payload, signature):
    if not signature:
        return False
    
    expected = 'sha256=' + hmac.new(
        WEBHOOK_SECRET.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, expected)

@app.route('/webhooks/lexamica/case-received', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Lexamica-Signature')
    
    # Verify signature using raw body
    if not verify_signature(request.get_data(), signature):
        return 'Invalid signature', 401
    
    # Parse the JSON body
    case_data = request.get_json()
    
    # Process asynchronously (in production, use a task queue)
    handle_case_received(case_data)
    
    return 'OK', 200

def handle_case_received(case_data):
    print(f"New case received: {case_data.get('lexamica_case_id')}")
    
    # Store in your database
    # send_to_database(case_data)
    
    # Notify staff
    # send_notification(case_data)

if __name__ == '__main__':
    app.run(port=3000)
```

***

## ❓ FAQ

### ❓ "How do I test my webhook endpoint?"

**📝 Full Answer:** Use the test endpoint to send a sample webhook:

```bash
curl -X POST \
  "https://integration.lexamica.com/organization/{orgId}/webhook-subscriptions/{subscriptionId}/test" \
  -H "Authorization: Bearer your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "lexamica_case_id": "test_123",
    "client_first_name": "Test",
    "client_last_name": "User",
    "practice_area": "Personal Injury"
  }'
```

***

### ❓ "Why is signature verification failing?"

**🔍 Quick Check:**

* [ ] Are you using the raw request body (not parsed JSON)?
* [ ] Is your secret exactly as set when creating the subscription?
* [ ] Are you using `sha256=` prefix in comparison?

***

### ❓ "What if my server is down?"

**📝 Full Answer:** Lexamica retries failed webhooks with exponential backoff:

* Immediate, +30s, +2min, +5min, +15min

If all retries fail, the event is logged. For guaranteed delivery, consider using polling as a backup.

***

## 🔧 Troubleshooting

**"Webhooks not arriving"**

* [ ] Is your endpoint publicly accessible (not localhost)?
* [ ] Is HTTPS enabled with a valid certificate?
* [ ] Is the subscription active? Check via GET `/webhook-subscriptions`

**"401 responses in Lexamica logs"**

* Your endpoint is returning 401—check signature verification logic

**"Processing takes too long"**

* Return 200 immediately, then process asynchronously
* Use a job queue (Bull, Celery, etc.) for heavy processing

***

## ➡️ Next Steps

* **Can't expose a webhook endpoint?** See [Receive Cases via Polling](/example-integrations/2b.-receive-cases-polling.md)
* **Want to accept/decline via API?** See [Handler Firm: Full Integration with Webhooks](/example-integrations/4a.-handler-firm-webhook.md)
* **Need to receive files?** See [File Operations](/example-integrations/apx-1.-file-operations.md)

***

*Last updated: January 2026*
