# data-model.md

_Last updated: 2026-08-15_
_Version: 1.0.0 — canonical: https://encrata.com/ai/data-model.md_

The shapes Encrata returns. Fields are only present when verified — a missing field means "unknown", never "guessed".

## Person profile (`POST /api/lookup`)

Every response also carries a top-level `request_id` (string, always present) — include it in any support request.

```json
{
  "request_id": "req_9f2a...",
  "email": "ada@figma.com",
  "validity": {
    "status": "valid",
    "provider": "Google Workspace",
    "confidence": "high",
    "disposable": false,
    "free_provider": false,
    "role_address": false,
    "catch_all": false
  },
  "identity": {
    "name": "Ada Lovelace",
    "gender": "female",
    "age_range": "30-39",
    "photos": ["https://..."],
    "bio": "Design systems lead...",
    "education": [{ "school": "...", "degree": "..." }]
  },
  "work": {
    "title": "Design Systems Lead",
    "seniority": "lead",
    "department": "design",
    "history": [{ "company": "...", "title": "...", "from": "2019", "to": "2023" }]
  },
  "company": {
    "name": "Figma",
    "domain": "figma.com",
    "size": "1001-5000",
    "industry": "Design software",
    "hq": "San Francisco, CA"
  },
  "social": [
    { "network": "linkedin", "url": "https://linkedin.com/in/..." },
    { "network": "github", "url": "https://github.com/..." }
  ],
  "location": { "city": "London", "region": "England", "country": "GB", "timezone": "Europe/London" },
  "breaches": {
    "count": 2,
    "sources": [
      { "name": "LinkedIn", "year": 2021, "exposed": ["email", "password_hash"] }
    ]
  }
}
```

## Validity verdict (`/api/email/validity`)

`status` ∈ `valid | invalid | catch-all | risky`, plus `reasons[]`, `domain_grade` (A–F), `confidence`, and domain hygiene signals (SPF, DKIM, DMARC, DNSSEC, mail servers, domain age, registrar).

## Breach record (`/api/email/breaches`)

Array of `{ name, year, exposed[] }` plus `count` and `first_seen`. A companion `services[]` lists platforms the address is registered on.

## Bulk job

`{ "job_id": "job_...", "status": "queued | processing | completed | failed | cancelled", "rows": 120000, "completed": 84000, "result_url": "https://..." }`

Reading a finished validity job: retrieve results with the export endpoint — `POST /api/agent/exports { "kind": "validity", "id": "<id>", "format": "xlsx" }` (poll `/status`, then `/download`). It returns the same flattened, spreadsheet-ready file the dashboard produces — one column per field — in `xlsx`, `csv`, or `json`.

## Field selection

When the request includes `"fields": ["identity", "company"]`, only the requested top-level
sections are returned; all others are omitted entirely (not null). `email` and
`request_id` are always present regardless of selection.

Allowed values for `fields` are:
`identity`, `work`, `company`, `social`, `location`, `validity`, `breaches`.

## Errors

Every error uses the same shape and carries a `request_id`:

```json
{ "request_id": "req_...", "error": { "code": "invalid_email", "message": "Not a valid email address." } }
```

Branch on `code`, never on `message`. Full status/code table: https://encrata.com/ai/api.md

## Types & nullability

- Strings unless noted. `count` is an integer; booleans are `true`/`false`.
- `validity.status` is one of `valid | invalid | catch-all | risky`.
- Arrays (`social`, `breaches.sources`, `work.history`) may be empty `[]` when verified-but-none, or absent when unknown.
- `photos`, `education`, `history` are optional and may be omitted.

## Conventions

- IDs are prefixed: `usr_`, `key_`, `job_`, `req_`.
- Timestamps are ISO 8601 UTC.
- Enums are lowercase snake_case strings.
- Results are scoped to the requesting workspace and are never shared across customers.
- In the profile JSON, `location` refers to the person's location, not the company's headquarters.
