Doc · SIEM/SOAR API

Identity exposure, in your pipeline.

A machine-to-machine endpoint for SIEM and SOAR. Send an email indicator, get back its public-exposure picture — sourced, corroborated, and timestamped. Deterministic by construction: no language model in the path, and never a singular verdict.

Early access. The contract is published below; request a key to get onboarded.

The contract

Evidence, not a verdict.

This endpoint exists to feed a decision, not to make one. It returns what the internet exposes about an indicator — never an identity attribution a SOAR could auto-action.

No identity attribution.

disambiguation.required is always true, and the response has no candidates field — by construction, not omission. Resolving a person stays a human-in-the-loop judgement your analysts make.

Deterministic, zero-LLM.

The exposure envelope is a direct projection of sourced findings. No model runs anywhere in this path. Every atom is cited.

Reliability ≠ maliciousness.

source_confidence is a source-reliability proxy, not a threat/identity score. corroboration_count is a separate trust signal — never folded into that number.

Tenant-isolated & on-prem-ready.

Keys are workspace-scoped; a scan from another workspace is invisible (404). The AGPL-3.0 core means you can run the whole thing inside your own perimeter.

Authentication

Authentication

Every request carries an X-API-Key header. API access requires the Team plan or above.

Getting a key

Mint a key from the dashboard Organization → API Access (SIEM/SOAR) tab. The plaintext key is shown once on creation — store it immediately. Keys can be rotated (atomic revoke-and-reissue) or soft-revoked; revocation preserves the audit trail.

X-API-Key: xpz_live_9f2c…   (shown once when you mint it)
Endpoint

POST /enrich

https://api.xpose.lu/api/v1/soc/enrich

Cache-first exposure lookup for an email indicator. A fresh result returns 200 with the full envelope; a cache miss returns 202 with a request_id and a poll_url. Results are cached ~24h by default.

curl -X POST https://api.xpose.lu/api/v1/soc/enrich \
  -H "X-API-Key: $XPOSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "indicator": { "type": "email", "value": "jane.doe@acme.com" }
  }'

indicator.type must be email (v1). An optional options.depth (default "quick") is accepted for forward compatibility.

200cache hit — full envelope
{
  "request_id": "8f14e45f-ceea-467d-9a3f-2b4c1d0e7a12",
  "status": "completed",
  "cache": "hit",
  "indicator": { "type": "email", "value": "jane.doe@acme.com" },
  "exposure": {
    "findings": [
      {
        "source": "hibp",
        "source_url": null,
        "source_confidence": 0.95,
        "source_tier": "high",
        "corroboration_count": 2,
        "corroborated_by": ["emailrep", "holehe"],
        "category": "breach",
        "severity": "high",
        "title": "Present in the LinkedIn 2021 breach",
        "indicator_type": "email",
        "indicator_value": "jane.doe@acme.com",
        "first_seen": "2026-05-02T09:14:00+00:00",
        "last_seen": "2026-06-28T22:41:00+00:00"
      }
    ],
    "total": 1,
    "by_severity": { "high": 1 }
  },
  "disambiguation": {
    "required": true,
    "reason": "person-resolution gated pending employer-domain disambiguation (R7); v1 returns indicator exposure only, no identity attribution"
  }
}
202cache miss — scan dispatched, poll the poll_url
{
  "status": "scanning",
  "request_id": "8f14e45f-ceea-467d-9a3f-2b4c1d0e7a12",
  "poll_url": "/api/v1/soc/enrich/8f14e45f-ceea-467d-9a3f-2b4c1d0e7a12",
  "indicator": { "type": "email", "value": "jane.doe@acme.com" }
}
Endpoint

GET /enrich/{request_id}

https://api.xpose.lu/api/v1/soc/enrich/{request_id}

Poll a cache-miss scan. While it runs you get a status; on completion you get the same envelope as a cache hit (with cache: "miss_scan_complete"). Scans are tenant-isolated — a request_id from another workspace returns 404.

curl https://api.xpose.lu/api/v1/soc/enrich/8f14e45f-ceea-467d-9a3f-2b4c1d0e7a12 \
  -H "X-API-Key: $XPOSE_API_KEY"
Still running200 { "status": "running", "request_id": "…" }

status echoes the scan: queued / scanning / running

Failed200 { "status": "failed", "request_id": "…" }
Completed200 + the full envelope (cache: "miss_scan_complete")
Field reference

The exposure envelope

Top level

request_idthe scan id (string, UUID)
statuscompleted on a resolved envelope
cachehit (fresh cache) or miss_scan_complete (freshly scanned)
indicatorechoes { type, value }
exposurethe findings block (below)
disambiguation{ required: true, reason } — always present, always required: true

exposure

findings[]array of finding atoms (below)
totalcount of atoms
by_severity{ severity: count }

Each finding atom

sourcethe module that produced it (e.g. hibp, holehe, emailrep)
source_urlprovenance URL, or null
source_confidence0–1 source-reliability proxy (not maliciousness/identity)
source_tierreliability tier for that source (e.g. high)
corroboration_counthow many independent sources agree (separate signal)
corroborated_by[]up to 5 corroborating source names
categoryfinding category (e.g. breach)
severitylow / medium / high (as emitted)
titlehuman-readable summary of the atom
indicator_type / indicator_valuethe atom's indicator, or null
first_seen / last_seenISO-8601 timestamps, or null
Errors

Errors

Every error returns a stable machine code a SOAR playbook can branch on:

{ "error": { "code": "rate_limited", "message": "Rate limit exceeded for this API key" } }
StatuscodeWhen
400invalid_emailIndicator value isn't a valid email
401invalid_api_keyMissing or unrecognised X-API-Key
403plan_requiredWorkspace plan is below Team
403forbidden(key management) caller isn't a workspace admin
404not_foundUnknown request_id, or one owned by another workspace
422unsupported_indicatorindicator.type isn't email (v1)
429rate_limitedPer-key hourly limit exceeded — see below
Rate limits

Rate limits

Each key has a per-hour, plan-aware limit. On exceed you get 429 with a Retry-After header (seconds). Limits are enforced per key; a transient infrastructure hiccup fails open rather than blocking your pipeline.

Key management

Managing keys

Keys are managed from the dashboard by a workspace admin (not via the exposure API). The management endpoints, all under https://api.xpose.lu/api/v1/workspaces/{workspace_id}/keys:

POST …/keysmint a named key (plaintext returned once)
GET …/keyslist keys (never returns the hash or plaintext)
POST …/keys/{key_id}/rotateatomic revoke-and-reissue
DELETE …/keys/{key_id}soft-revoke (preserves the audit trail)

Wire your pipeline to the exposure layer.

Request a key and we'll onboard your SIEM/SOAR integration — days, not weeks.