The Entity Enricher REST API lets you enrich entities, manage schemas, and retrieve records programmatically. All responses are JSON. Real-time progress uses Server-Sent Events (SSE).
Integrate Entity Enricher in three steps:
GET /api/schema/savedList saved schemas or generate one from sample data
POST /api/single/enrich/streamStart enrichment, get a job ID for SSE streaming
GET /api/records/{id}Retrieve the full enrichment record with structured output
All API endpoints (except login/register) require authentication. Use the X-API-Key header with an organization access key:
curl -H "X-API-Key: ent_your_key_here" \
https://your-instance.example.com/api/enrichment/optionsCreate API keys from the API Keys page or via POST /api/auth/api-keys. See the API Keys guide for details on key types and permissions.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/enrichment/options | Available models, languages, and strategies |
| POST | /api/single/enrich/stream | Start single entity enrichment (returns job_id for SSE) |
| POST | /api/single/enrich/sync | Blocking single enrichment for non-SSE clients (Make.com, curl) |
| POST | /api/enrichment/batch/start | Start batch enrichment for multiple entities |
| POST | /api/enrichment/batch/fetch | Fetch entities from an external URL |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/llm/stream/{job_id} | SSE stream for any LLM job (enrichment, schema, fusion) |
| POST | /api/llm/cancel/{job_id} | Cancel a running or paused job |
| POST | /api/llm/continue/{job_id} | Resume a paused job (e.g., after classification mismatch) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/schema/saved | List all saved schemas |
| POST | /api/schema/saved | Create a new schema |
| POST | /api/schema/generate/stream | Generate schema from sample data (SSE) |
| POST | /api/schema/saved/{id}/prompt/stream | AI-edit schema with natural language (SSE) |
| POST | /api/schema/analyze-sample | Analyze sample JSON for ambiguous property names — those admitting several readings in the context of their parent, or none — and for related items mixing entity facts with per-parent facts (stateless report, suggested renames) |
| POST | /api/schema/saved/{id}/analyze | Run the ambiguity and identity-scoping checks on a saved schema and write their annotations (a rewritten description per ambiguous name) |
| POST | /api/schema/scoping-split | Apply one identity-scoping split to a sample set — the related entity's own facts move into their own subobject (deterministic, free, nothing saved) |
| DELETE | /api/schema/saved/{id}/enrichment-data | Purge a schema's enrichment data — records and entity state — keeping the schema (owner+) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/records | List records with pagination and filtering |
| GET | /api/records/{id} | Get full record detail with structured output |
| POST | /api/records/batch-delete | Delete multiple records (max 100) |
| POST | /api/fusion/merge | Merge results from multiple models |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/attachments | Upload one or more files (multipart/form-data) |
| POST | /api/attachments/base64 | Upload one file via JSON base64 (for non-multipart clients) |
| GET | /api/attachments/{id}/download | Download the original file bytes |
| DELETE | /api/attachments/{id} | Delete an attachment (post-enrichment cleanup) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/schema/saved/{id}/publish | Publish a linked schema’s working copy as the contract enrichment and its databases run against. Nothing structural takes effect until this runs |
| POST | /api/schema/sample/generate/stream | Generate 1..N sample JSON objects of one entity type (returns job_id for SSE) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/databases | List the organization’s database registrations, with pending delta counts |
| POST | /api/databases | Register a database on a schema |
| GET | /api/databases/{id}/snapshot | Download the full state as a .sql snapshot — bootstrap from zero |
| GET | /api/databases/{id}/changes | Fetch the next FIFO window of deltas; claim them to lease for acknowledged delivery |
| POST | /api/databases/{id}/ack | Acknowledge applied deltas up to an id — releases the lease |
| POST | /api/databases/{id}/clear-acked | Purge delivered and acknowledged deltas |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/semantic-concepts | Browse the concept vocabulary, filtered by type and scored against a reference concept |
| GET | /api/semantic-concepts/types | List concept types with their counts and embedding models |
| POST | /api/semantic-concepts/probe | Dry-run the resolution ladder for a text — what would it match, and how closely |
| GET | /api/semantic-concepts/duplicates | Concept pairs sitting just below the merge threshold |
| POST | /api/semantic-concepts/import | Batch-resolve a CSV of identity texts (minting requires owner) |
| GET | /api/semantic-concepts/export | Export the vocabulary as CSV |
| POST | /api/semantic-concepts/delete-impact | What deleting concepts would affect — usage counts and resync cost |
| GET | /api/semantic-concepts/migration/status | State of the embedding-model migration, if one is running |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/benchmarks | List benchmark scenarios |
| POST | /api/benchmarks/{id}/run | Run a scenario across models — each result is scored automatically |
| POST | /api/benchmarks/{id}/reference | Save and verify the gold reference a scenario is scored against |
| GET | /api/billing/balance | Current credit balance |
| GET | /api/billing/transactions | Credit transaction history, including embedding spend |
| GET | /api/billing/plans | Available plans and their limits |
Enrichment, schema generation, and fusion operations use Server-Sent Events for real-time progress. Start a job, get a job_id, then connect to the SSE stream:
| Event | Description |
|---|---|
| model_started | Model processing begins |
| expertise_completed | One expertise domain finished (with partial results) |
| model_completed | Model finished with result, record_id, and cost |
| fusion_started / fusion_completed | Multi-model fusion lifecycle events |
| entity_started / entity_completed | Batch-specific per-entity events (include entity_index) |
| completed | Terminal event - close the connection |
| error | Job-level error occurred |
A complete workflow that lists schemas, starts enrichment, streams results, and retrieves the final record:
import httpx
import json
BASE = "https://your-instance.example.com"
KEY = "ent_your_api_key"
HEADERS = {"X-API-Key": KEY, "Content-Type": "application/json"}
# 1. List saved schemas
schemas = httpx.get(f"{BASE}/api/schema/saved", headers=HEADERS).json()
schema_id = schemas["schemas"][0]["id"]
# 2. Start enrichment
resp = httpx.post(f"{BASE}/api/single/enrich/stream", headers=HEADERS, json={
"entity_data": {"name": "Moderna Inc", "country": "US"},
"schema_id": schema_id,
"models": ["anthropic::claude-sonnet-4-5-20250514"],
"strategy": "multi_expertise",
})
job_id = resp.json()["job_id"]
# 3. Stream SSE events
record_id = None
with httpx.stream("GET", f"{BASE}/api/llm/stream/{job_id}", headers=HEADERS) as stream:
for line in stream.iter_lines():
if not line.startswith("data: "):
continue
event = json.loads(line[6:])
if event["type"] == "model_completed" and event.get("record_id"):
record_id = event["record_id"]
elif event["type"] == "completed":
break
# 4. Retrieve the enrichment record
if record_id:
record = httpx.get(f"{BASE}/api/records/{record_id}", headers=HEADERS).json()
print(json.dumps(record["structured_output"], indent=2))Start a batch enrichment with two models and stream the results:
# Start batch enrichment
JOB_ID=$(curl -s -X POST \
-H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
"$BASE/api/enrichment/batch/start" \
-d '{
"entities": [
{"name": "Pfizer Inc", "country": "US"},
{"name": "Roche", "country": "CH"}
],
"schema_id": "your-schema-uuid",
"models": ["anthropic::claude-sonnet-4-5-20250514", "openai::gpt-4o"],
"strategy": "multi_expertise",
"arbitration_model": "anthropic::claude-sonnet-4-5-20250514"
}' | jq -r '.job_id')
# Stream events
curl -N -H "X-API-Key: $KEY" "$BASE/api/llm/stream/$JOB_ID"
# List resulting records
curl -s -H "X-API-Key: $KEY" \
"$BASE/api/records?type=enrichment&page_size=10" | jq '.records'| Status | Meaning | Example |
|---|---|---|
| 200 | Success | Request completed |
| 400 | Bad request | Invalid model key or missing field |
| 401 | Unauthorized | Missing or invalid API key |
| 402 | Payment required | Plan limit or credit balance — quota exhausted, too many models or languages, a feature not in your plan. The body carries a machine-readable code alongside detail. |
| 403 | Forbidden | Insufficient role for this endpoint |
| 404 | Not found | Record, schema, or job not found |
| 500 | Server error | Internal failure |
Error responses include a detail field with a human-readable error message. Plan and billing failures (402) additionally carry a structured body with a stable code — prompt_limit_reached, insufficient_credits, model_limit_exceeded, benchmarks_not_in_plan — plus the relevant limit and usage numbers, so a client can branch on the cause instead of parsing prose. SSE streams emit an error event type before the terminal completed event if something fails mid-stream.
Models are identified by composite keys in the format provider_name::model_name. Use GET /api/enrichment/options to list available models and their keys.
The model parameter is optional on enrichment, schema generation, and sample generation: omit it (or pass the literal "auto") and the server picks your organization's default model — the pinned per-task default if one is set in Settings, otherwise the model with the best overall score from your scoring-source benchmarks. The options response's default_models field shows what auto currently resolves to, and a model_auto_selected SSE event reports the pick on every job. Auto always resolves to a single model (it never triggers fusion); for reproducible pipelines, keep passing explicit models.
Request options constrain the auto pick: with enable_web_search: true only web-search-capable models are considered (the options response's default_models_web_search field previews that pick), and binary attachments require a model that can read them (PDF, vision, audio). When no eligible model satisfies the constraints the request fails with HTTP 400 no_capable_default_model instead of silently dropping the option.
anthropic::claude-sonnet-4-5-20250514openai::gpt-4ogoogle::gemini-2.5-prodeepseek::deepseek-chatThe application includes interactive API documentation with request/response examples. Requires admin authentication to access: