---
title: Enterprise Reference
path: reference/enterprise
status: published
---

Legal hold, retention policies, DLP, eDiscovery, audit, and vectorization policies.

**Base paths:** `/api/v1/enterprise/`, `/api/v1/vectorization/`, `/api/v1/admin/vectorization/`

All endpoints require `tenant:admin` or higher unless noted.

## Legal holds

### POST /api/v1/enterprise/legal-holds

Create a legal hold. Returns 201.

**Body:**

| Field | Notes |
|-------|-------|
| `name` | Hold identifier |
| `description` | |
| `matter_id` | External matter/case ID |
| `custodian_ids` | Array of user IDs |
| `legal_counsel` | Contact info |
| `expiration_date` | ISO timestamp (optional; holds can be indefinite) |

### GET /api/v1/enterprise/legal-holds

List holds.

### GET /api/v1/enterprise/legal-holds/{hold_id}

Detail including scope and item counts.

### PATCH /api/v1/enterprise/legal-holds/{hold_id}

Update metadata.

### POST /api/v1/enterprise/legal-holds/{hold_id}/release

Release the hold. Releases the underlying content for normal deletion/retention. Emits a `compliance.legal_hold_release` audit event at `WARNING` severity (the protection coming off is a significant action).

### POST /api/v1/enterprise/legal-holds/{hold_id}/items

Add items to hold. Returns 201.

**Body:**

```json
{
  "scope_type": "user",
  "scope_id": "usr_01J4M",
  "include_pattern": "**/*",
  "exclude_pattern": "Trash/**"
}
```

`scope_type`: `user`, `group`, `share`, `folder`.

### GET /api/v1/enterprise/legal-holds/{hold_id}/items

List items in hold.

### DELETE /api/v1/enterprise/legal-holds/{hold_id}/items/{item_id}

Returns 204.

## Retention policies

### POST /api/v1/enterprise/retention-policies

**Body:**

| Field | Notes |
|-------|-------|
| `name` | |
| `description` | |
| `retention_days` | How long to retain |
| `trigger` | `creation`, `modification` |
| `action` | `delete`, `archive`, `quarantine` |
| `scope_type` | `tenant`, `share`, `folder`, `user` |
| `scope_ids` | Array of scope target IDs |

Returns 201.

### GET /api/v1/enterprise/retention-policies

### PATCH /api/v1/enterprise/retention-policies/{policy_id}

### DELETE /api/v1/enterprise/retention-policies/{policy_id}

Returns 204.

## DLP

### POST /api/v1/enterprise/dlp/rules

Create a DLP rule.

**Body:**

| Field | Notes |
|-------|-------|
| `name` | Required |
| `description` | |
| `rule_type` | `regex`, `keyword`, `dictionary`, `file_type` |
| `pattern` | Required. The regex, keyword list, or rule payload |
| `case_sensitive` | Default false |
| `apply_to_uploads` | Default true |
| `apply_to_downloads` | Default false |
| `apply_to_shares` | Default true |
| `file_types` | Optional MIME filter |
| `action` | `block`, `quarantine`, `notify`, `log` (default `log`) |
| `severity` | `low`, `medium`, `high` (default `medium`) |
| `notify_user` | Default true |
| `notify_admins` | Default true |
| `notify_emails` | Additional notification recipients |
| `priority` | Higher runs first |

Returns 201.

### GET /api/v1/enterprise/dlp/rules

List rules. **Query:** `enabled_only` (default true), `rule_type`.

### GET /api/v1/enterprise/dlp/rules/{rule_id}

Single rule.

### PATCH /api/v1/enterprise/dlp/rules/{rule_id}

Update a DLP rule. All fields optional; omitted fields stay as-is. Returns the updated rule.

**Body:** any of the fields from `POST` plus `enabled` (toggle without recreating).

Audit detail policy: the literal `pattern` string is **not** written to the audit log — `details.pattern_changed: true` is recorded instead. DLP patterns themselves can be sensitive (a regex for an internal token format would leak the format if logged), so this preserves the change record without leaking the rule's secret-detection logic.

### DELETE /api/v1/enterprise/dlp/rules/{rule_id}

Delete a rule. Returns 204.

### GET /api/v1/enterprise/dlp/violations

List DLP violations.

**Query:** `rule_id`, `user_id`, `share_id`, `status` (`detected`, `reviewed`, `dismissed`, `escalated`, `resolved`), `severity`, `limit`, `offset`.

**Response:** A list of violations. Each violation: `id`, `rule_id`, `file_id`, `file_name`, `share_id`, `severity`, `action_taken`, `match_count`, `match_context`, `user_id`, `status`, `created_at`.

### PATCH /api/v1/enterprise/dlp/violations/{violation_id}

Update violation status after review.

**Body:** `status` (`reviewed`, `dismissed`, `escalated`, `resolved`), optional `review_notes`.

## eDiscovery

eDiscovery resources are nested under cases: searches, custodians, and exports each belong to a parent case.

### POST /api/v1/enterprise/ediscovery/cases

Create case.

**Body:** `name`, `description`, `case_number`, `matter_type`, `external_counsel`, `opposing_counsel`, `create_legal_hold` (default true).

Returns 201.

### GET /api/v1/enterprise/ediscovery/cases

List cases. **Query:** `status`, `include_closed`.

### GET /api/v1/enterprise/ediscovery/cases/{case_id}

Single case with full details.

### POST /api/v1/enterprise/ediscovery/cases/{case_id}/close

Close a case. **Query:** `release_hold` (default true) — also releases the associated legal hold.

### POST /api/v1/enterprise/ediscovery/cases/{case_id}/custodians

Add a custodian. **Body:** `user_id`, `user_email`, `user_name`, `apply_hold` (default true), `notes`.

### DELETE /api/v1/enterprise/ediscovery/cases/{case_id}/custodians/{custodian_id}

Remove a custodian. **Query:** `release_hold` (default true).

### POST /api/v1/enterprise/ediscovery/cases/{case_id}/searches

Create a saved search. **Body:** `name`, `query`, `semantic_query`, `date_from`, `date_to`, `file_types`, `custodian_ids`, `share_ids`, `include_deleted`, `include_versions`.

### POST /api/v1/enterprise/ediscovery/searches/{search_id}/run

Run a search and return results. **Query:** `limit`, `offset`.

### POST /api/v1/enterprise/ediscovery/cases/{case_id}/exports

Create an export job. Emits `compliance.ediscovery_export_create` at `WARNING` severity (data leaving the platform).

**Body:** `name`, optional `search_id`, `format` (`native`, `pdf`, `concordance`, `edrm`), `include_metadata`, `include_audit_trail`.

### GET /api/v1/enterprise/ediscovery/cases/{case_id}/exports

List exports for a case.

## Audit log

### GET /api/v1/enterprise/audit/events

Query audit events.

**Query:**

| Param | Notes |
|-------|-------|
| `category` | One of: `authentication`, `authorization`, `file_access`, `file_modification`, `sharing`, `admin`, `security`, `compliance` |
| `severity` | `info`, `warning`, `error`, `critical` |
| `user_id` | Filter by actor |
| `share_id` | Filter by share |
| `resource_type` / `resource_id` | Filter by target |
| `event_type` | e.g. `file.create`, `compliance.legal_hold_release` |
| `since` | ISO timestamp |
| `until` | ISO timestamp |
| `outcome` | `success`, `failure`, `denied`, `detected`, `remediated`, `dismissed` |
| `limit` | 1–1000, default 100 |
| `offset` | |

**Response:** a list of audit-event objects.

```json
[
  {
    "id": "aud_01J3L",
    "tenant_id": "tnt_01J3A",
    "event_type": "file.create",
    "category": "file_modification",
    "severity": "info",
    "user_id": "usr_01J3N",
    "user_email": "alice@example.com",
    "user_name": "Alice",
    "service_account": "3seej56cg90454l845dt93rfilywskbw",
    "ip_address": "203.0.113.7",
    "user_agent": "ScaiSpeak/1.0",
    "client_type": null,
    "resource_type": "file",
    "resource_id": "fil_01J3M",
    "resource_name": "scaispeak_job_42.mp3",
    "share_id": "shr_01J3K",
    "action": "create",
    "outcome": "success",
    "details": {"version": 1, "size": 22300000, "mime_type": "audio/mpeg"},
    "event_time": "2026-04-23T10:15:05Z",
    "request_id": null
  }
]
```

`service_account` carries the RFC 8693 `act.client_id` from the JWT when the call reached ScaiDrive through a token-exchange flow (delegating service identifier). It is null for direct user logins. See [Enterprise Compliance — delegation provenance](/docs/scaidrive/advanced/enterprise-compliance) for the full flow.

### GET /api/v1/enterprise/audit/stats

Aggregate counts of audit events by category and severity.

**Query:** `days` (1–365, default 30).

**Response:** `{by_category: {…}, by_severity: {…}, total: N}`.

## Vectorization

### GET /api/v1/vectorization/policies

List vectorization policies in the tenant (any authenticated user).

### GET /api/v1/vectorization/providers

List embedding providers available.

**Response:**

```json
{
  "providers": [
    {"name": "openai", "model_name": "text-embedding-3-small", "embedding_dimension": 1536, "is_default": true}
  ]
}
```

### POST /api/v1/admin/vectorization/policies

Create policy. Admin only.

**Body:**

| Field | Notes |
|-------|-------|
| `name`, `description` | |
| `scope_type` | `tenant`, `share`, `folder` |
| `scope_ids` | Array |
| `file_type_patterns` | `["application/pdf", "text/*"]` |
| `exclude_patterns` | |
| `chunking_strategy` | `by_paragraph`, `by_sentence`, `fixed_size` |
| `chunk_size` | Tokens for `fixed_size` |
| `chunk_overlap` | Tokens |
| `provider_id` | Embedding provider |
| `auto_index` | Default true |
| `schedule` | Cron expression |

Returns 201.

### PATCH /api/v1/admin/vectorization/policies/{policy_id}

### DELETE /api/v1/admin/vectorization/policies/{policy_id}

## Error codes

| Code | HTTP | When |
|------|------|------|
| `RETENTION_BLOCKED_DELETION` | 403 | Delete blocked by retention policy |
| `LEGAL_HOLD_BLOCKED_DELETION` | 403 | Delete blocked by legal hold |
| `DLP_RULE_BLOCKED` | 403 | Action blocked by DLP rule |

## Related

- [Enterprise Compliance](/docs/scaidrive/advanced/enterprise-compliance)
- [Search Reference](/docs/scaidrive/reference/search) — how policies drive indexing.