Email DLP API
The Email DLP API provides admin endpoints for managing email quarantine, per-org relay configuration, and compliance templates. All endpoints require admin authentication (Authorization: Bearer <admin-token>).
For setup instructions, see Email DLP Setup. For configuration guidance, see Email DLP Configuration. For quarantine workflows, see Email Quarantine Management.
Endpoints
Section titled “Endpoints”Quarantine
Section titled “Quarantine”| Method | Path | Description |
|---|---|---|
GET |
/v1/admin/email/quarantine |
List quarantined emails |
GET |
/v1/admin/email/quarantine/stats |
Aggregated quarantine statistics |
GET |
/v1/admin/email/quarantine/{scan_id} |
Get quarantine detail |
GET |
/v1/admin/email/quarantine/{scan_id}/eml |
Download raw .eml file |
POST |
/v1/admin/email/quarantine/{scan_id}/release |
Release and re-deliver |
DELETE |
/v1/admin/email/quarantine/{scan_id} |
Soft-delete |
Configuration
Section titled “Configuration”| Method | Path | Description |
|---|---|---|
GET |
/v1/admin/email/config |
List all org configs |
GET |
/v1/admin/email/config/defaults |
Get default config values |
GET |
/v1/admin/email/config/{org_id} |
Get org config |
PUT |
/v1/admin/email/config/{org_id} |
Create or update org config |
DELETE |
/v1/admin/email/config/{org_id} |
Delete org config (revert to defaults) |
Overview
Section titled “Overview”| Method | Path | Description |
|---|---|---|
GET |
/v1/admin/email/overview |
Per-org dashboard summary |
Quarantine object
Section titled “Quarantine object”The QuarantineItemResponse returned in list results:
| Field | Type | Description |
|---|---|---|
scan_id |
string |
Unique DLP scan ID (max 64 chars) |
org_id |
int | null |
Owning organization ID |
sender |
string | null |
Envelope sender address (RFC 5321) |
recipients |
list[string] | null |
Recipient addresses |
subject |
string | null |
Email subject line |
risk_level |
string |
DLP risk: none, low, medium, high, critical |
action |
string |
Disposition taken: quarantine, reject |
entity_count |
int |
Number of detected sensitive entities |
quarantined_at |
datetime | null |
When the email was quarantined |
released_at |
datetime | null |
When (if) released for delivery |
released_by |
uuid | null |
Admin user ID who released |
The QuarantineDetailResponse extends the above with:
| Field | Type | Description |
|---|---|---|
entities |
list[object] |
Full list of detected entity dicts (entity_type, confidence, detection_tier, span offsets) |
attachment_findings |
list[object] |
Per-attachment scan results (filename, status, findings) |
eml_path |
string | null |
Filesystem path to the raw .eml file |
GET /v1/admin/email/quarantine
Section titled “GET /v1/admin/email/quarantine”List quarantined emails with pagination, filtering, and sorting.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
int |
1 |
Page number (1-indexed) |
page_size |
int |
25 |
Results per page (1–100) |
offset |
int |
— | Legacy offset override (takes precedence over page) |
limit |
int |
— | Legacy limit override (1–200, takes precedence over page_size) |
org_id |
int |
— | Exact-match filter on organization |
risk_level |
string |
— | Exact-match filter: none, low, medium, high, critical |
action |
string |
— | Exact-match filter: quarantine, reject |
sender_domain |
string |
— | Match sender domain (SQL LIKE %@{domain}) |
date_from |
datetime |
— | quarantined_at >= date_from |
date_to |
datetime |
— | quarantined_at <= date_to |
has_attachments |
bool |
— | Filter by whether attachment_findings is non-empty |
sort_by |
string |
quarantined_at |
Sort column: quarantined_at, risk_level, sender |
sort_order |
string |
desc |
Sort direction: asc, desc |
Response (200 OK):
{ "items": [ { "scan_id": "scan_01HXYZ", "org_id": 42, "subject": "Patient update - urgent", "risk_level": "high", "action": "quarantine", "entity_count": 2, "quarantined_at": "2026-03-20T09:14:22Z", "released_at": null, "released_by": null } ], "total_count": 1, "page": 1, "page_size": 25, "total": 1, "offset": 0, "limit": 25}The total/offset/limit fields are legacy compatibility aliases for total_count/computed offset/effective limit.
GET /v1/admin/email/quarantine/stats
Section titled “GET /v1/admin/email/quarantine/stats”Aggregated statistics for all non-deleted quarantined emails. All aggregation is performed server-side.
Response (200 OK):
{ "total_quarantined": 842, "by_risk_level": { "critical": 12, "high": 187, "medium": 501, "low": 142 }, "by_action": { "quarantine": 700, "reject": 142 }, "by_org": { "42": 412, "58": 430 }, "time_series_7d": [ {"date": "2026-03-17", "count": 98}, {"date": "2026-03-18", "count": 115}, {"date": "2026-03-19", "count": 107}, {"date": "2026-03-20", "count": 143}, {"date": "2026-03-21", "count": 132}, {"date": "2026-03-22", "count": 119}, {"date": "2026-03-23", "count": 128} ], "time_series_30d": ["..."], "top_entity_types": [ {"entity_type": "ssn", "count": 234}, {"entity_type": "health_info", "count": 198}, {"entity_type": "credit_card", "count": 87} ], "avg_scan_latency_ms": 108.4}| Field | Type | Description |
|---|---|---|
total_quarantined |
int |
Total non-deleted quarantine records |
by_risk_level |
dict[string, int] |
Count grouped by risk level |
by_action |
dict[string, int] |
Count grouped by action |
by_org |
dict[int, int] |
Count grouped by organization ID |
time_series_7d |
list[object] |
Daily counts for the last 7 days |
time_series_30d |
list[object] |
Daily counts for the last 30 days |
top_entity_types |
list[object] |
Most frequently detected entity types |
avg_scan_latency_ms |
float | null |
Average DLP scan duration in milliseconds |
GET /v1/admin/email/quarantine/{scan_id}
Section titled “GET /v1/admin/email/quarantine/{scan_id}”Get full detail for a single quarantined email, including entities and attachment findings.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
scan_id |
string |
DLP scan ID |
Response (200 OK):
Returns QuarantineDetailResponse — see Quarantine object above.
Errors:
| Status | Condition |
|---|---|
404 |
Quarantined email not found |
GET /v1/admin/email/quarantine/{scan_id}/eml
Section titled “GET /v1/admin/email/quarantine/{scan_id}/eml”Download the raw .eml file for a quarantined email.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
scan_id |
string |
DLP scan ID |
Response: application/octet-stream
The response body is the raw .eml bytes. The Content-Disposition header is set to attachment; filename="{scan_id}.eml".
curl -s "https://platform.arbitex.ai/v1/admin/email/quarantine/scan_01HXYZ/eml" \ -H "Authorization: Bearer <admin-token>" \ -o scan_01HXYZ.emlErrors:
| Status | Condition |
|---|---|
404 |
Quarantined email not found, no .eml path stored, or .eml file missing from disk |
POST /v1/admin/email/quarantine/{scan_id}/release
Section titled “POST /v1/admin/email/quarantine/{scan_id}/release”Release a quarantined email — re-deliver via SMTP to the downstream MTA. The original .eml is transmitted with X-Arbitex-DLP-Released headers appended. An audit event (email_quarantine_released) is recorded.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
scan_id |
string |
DLP scan ID to release |
No request body required.
Response (200 OK):
{ "status": "released", "scan_id": "scan_01HXYZ"}Errors:
| Status | Condition |
|---|---|
404 |
Quarantined email not found or already released |
502 |
SMTP delivery to downstream MTA failed — email remains quarantined |
DELETE /v1/admin/email/quarantine/{scan_id}
Section titled “DELETE /v1/admin/email/quarantine/{scan_id}”Soft-delete a quarantined email. Sets deleted_at on the record. The email is excluded from list queries and stats. An audit event (email_quarantine_deleted) is recorded.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
scan_id |
string |
DLP scan ID to delete |
Response (200 OK):
{ "status": "deleted", "scan_id": "scan_01HXYZ"}Errors:
| Status | Condition |
|---|---|
404 |
Quarantined email not found |
EmailRelayConfig object
Section titled “EmailRelayConfig object”Per-org email relay configuration stored in the email_relay_config table:
| Field | Type | Default | Validation | Description |
|---|---|---|---|---|
id |
int |
— | Auto-increment PK | Config record ID |
org_id |
int |
— | FK to organizations.id, unique |
Organization this config applies to |
enabled |
bool |
true |
— | Enable email DLP scanning for this org |
allowed_sender_domains |
list[string] |
[] |
Regex-validated domain format | Sender domain allowlist. Empty = all domains accepted |
quarantine_policy |
string |
"quarantine" |
quarantine, reject, tag |
Action on high-risk findings |
max_attachment_size_mb |
int |
10 |
1–50 | Max per-attachment size in MB |
custom_risk_thresholds |
object | null |
null |
— | Per-entity-type confidence overrides |
rate_limit_per_minute |
int |
60 |
1–10,000 | Sustained message rate limit |
rate_limit_burst |
int |
120 |
1–20,000 | Burst capacity above sustained rate |
The response also includes:
| Field | Type | Description |
|---|---|---|
applied_templates |
list[object] |
Active compliance templates — each has template_id (string) and rule_count (int) |
GET /v1/admin/email/config
Section titled “GET /v1/admin/email/config”List all per-org email relay configurations.
Response (200 OK): Array of EmailRelayConfigResponse objects.
GET /v1/admin/email/config/defaults
Section titled “GET /v1/admin/email/config/defaults”Return the default email relay configuration values. These are the values used for any organization without a per-org config record. The response is static — no database query needed.
Response (200 OK):
{ "enabled": true, "allowed_sender_domains": [], "quarantine_policy": "quarantine", "max_attachment_size_mb": 10, "custom_risk_thresholds": null, "rate_limit_per_minute": 60, "rate_limit_burst": 120}GET /v1/admin/email/config/{org_id}
Section titled “GET /v1/admin/email/config/{org_id}”Get email relay configuration for a specific organization.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
org_id |
int |
Organization ID |
Response (200 OK): EmailRelayConfigResponse including applied_templates.
Errors:
| Status | Condition |
|---|---|
404 |
No email config exists for org_id |
PUT /v1/admin/email/config/{org_id}
Section titled “PUT /v1/admin/email/config/{org_id}”Create or update email relay configuration for an organization. Idempotent: creates if no config exists, updates otherwise. Returns 422 on validation failure.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
org_id |
int |
Organization ID |
Request body:
{ "enabled": true, "allowed_sender_domains": ["acme.com", "partner.org"], "quarantine_policy": "quarantine", "max_attachment_size_mb": 20, "custom_risk_thresholds": null, "rate_limit_per_minute": 200, "rate_limit_burst": 400}All fields have defaults, so you only need to include fields you want to override.
Domain validation: Each domain in allowed_sender_domains is validated against the pattern ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$.
Response (200 OK): Updated EmailRelayConfigResponse including applied_templates.
Errors:
| Status | Condition |
|---|---|
404 |
Organization org_id does not exist |
422 |
Validation failure (invalid domain format, out-of-range values, invalid quarantine_policy) |
DELETE /v1/admin/email/config/{org_id}
Section titled “DELETE /v1/admin/email/config/{org_id}”Delete email relay configuration for an organization, reverting to system defaults.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
org_id |
int |
Organization ID |
Response: 204 No Content
Errors:
| Status | Condition |
|---|---|
404 |
No email config exists for org_id |
GET /v1/admin/email/overview
Section titled “GET /v1/admin/email/overview”Per-org email DLP summary for the admin dashboard. Returns all organizations with relay config status, active quarantine counts, 24-hour scan volume, and applied compliance templates.
Response (200 OK):
{ "orgs": [ { "org_id": 42, "org_name": "Acme Corp", "enabled": true, "quarantine_policy": "quarantine", "templates_applied": ["hipaa", "pci_dss"], "quarantine_count": 23, "scan_volume_24h": 412 } ], "global_enabled": true, "total_quarantine_count": 23}| Field | Type | Description |
|---|---|---|
orgs |
list[OrgEmailOverview] |
Per-org summary (all orgs, defaults applied where no config row) |
global_enabled |
bool |
System-wide email_dlp.enabled toggle |
total_quarantine_count |
int |
Sum of active quarantine records across all orgs |
OrgEmailOverview fields:
| Field | Type | Description |
|---|---|---|
org_id |
int |
Organization ID |
org_name |
string |
Organization display name |
enabled |
bool |
Whether email DLP scanning is enabled for this org |
quarantine_policy |
string |
Active quarantine policy |
templates_applied |
list[string] |
Compliance template IDs active for this org |
quarantine_count |
int |
Active (non-deleted, non-released) quarantine records |
scan_volume_24h |
int |
Quarantine records created in the last 24 hours |