Skip to content

Entities API

Query, search, and manage canonical entities.

GET /v1/entities

Search and filter entities across all sources.

Query Parameters

ParameterTypeDefaultDescription
qstringN/ASearch query (matches against canonical data and entity type)
entity_typestringN/AFilter by entity type (e.g., "customer")
include_externalbooleanfalseInclude external entities in results
limitint50Max results (1--1000)
offsetint0Pagination offset

Request

python
from kanoniv.client import KanonivClient

client = KanonivClient(api_key="kn_...", base_url="https://api.kanoniv.com")

# Search by email
results = client.get("/v1/entities", params={
    "q": "[email protected]",
    "entity_type": "customer",
    "limit": 10
})

for item in results["data"]:
    if item["kind"] == "Canonical":
        entity = item["entity"]
        print(f"{entity['id']}: {entity['canonical_data']['email']}")

print(f"Total: {results['total']}")
bash
curl "https://api.kanoniv.com/v1/[email protected]&entity_type=customer&limit=10" \
  -H "X-API-Key: kn_..."

Response

json
{
  "data": [
    {
      "kind": "Canonical",
      "entity": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "tenant_id": "t_abc123",
        "entity_type": "customer",
        "canonical_data": {
          "email": "[email protected]",
          "name": "John Doe",
          "company": "Acme Corp"
        },
        "field_provenance": {
          "email": "Stripe",
          "name": "Salesforce",
          "company": "Salesforce"
        },
        "confidence_score": 0.94,
        "is_locked": false,
        "created_at": "2026-01-15T10:00:00Z",
        "updated_at": "2026-02-01T09:00:00Z"
      }
    }
  ],
  "total": 1
}

When include_external is true, external entities are included as additional entries:

json
{
  "kind": "External",
  "entity": {
    "id": "ext_001",
    "tenant_id": "t_abc123",
    "data_source_id": "src_stripe",
    "external_id": "cus_123",
    "entity_type": "customer",
    "raw_data": { "email": "[email protected]" },
    "normalized_data": { "email": "[email protected]", "name": "John Doe" },
    "ingested_at": "2026-01-15T10:00:00Z",
    "batch_id": "batch_001"
  }
}

Response Fields

Each item in data is a tagged union with kind and entity:

FieldTypeDescription
kindstring"Canonical" or "External"
entityobjectThe entity object (schema depends on kind)
totalintTotal matching results across both types

See Resolution API for the full field reference of canonical and external entity objects.


GET /v1/entities/:id/history

View the audit trail for an entity: all recorded events including field updates, merges, splits, and reverts.

Path Parameters

ParameterTypeRequiredDescription
idUUIDYesCanonical entity ID

Request

python
history = client.get("/v1/entities/550e8400-e29b-41d4-a716-446655440000/history")

for event in history:
    print(f"[{event['timestamp']}] {event['action']} by {event['actor_type']}")
    if event.get("reason"):
        print(f"  Reason: {event['reason']}")
bash
curl "https://api.kanoniv.com/v1/entities/550e8400-e29b-41d4-a716-446655440000/history" \
  -H "X-API-Key: kn_..."

Response

Returns an array of audit events ordered by timestamp descending (most recent first):

json
[
  {
    "id": "evt_001",
    "tenant_id": "t_abc123",
    "actor_id": "user_001",
    "actor_type": "user",
    "action": "field_update",
    "resource_type": "canonical_entity",
    "resource_id": "550e8400-e29b-41d4-a716-446655440000",
    "before_state": { "email": "[email protected]" },
    "after_state": { "email": "[email protected]" },
    "reason": null,
    "timestamp": "2026-02-01T09:00:00Z"
  },
  {
    "id": "evt_002",
    "tenant_id": "t_abc123",
    "actor_id": "system",
    "actor_type": "worker",
    "action": "merge",
    "resource_type": "canonical_entity",
    "resource_id": "550e8400-e29b-41d4-a716-446655440000",
    "before_state": null,
    "after_state": { "email": "[email protected]", "name": "John Doe" },
    "reason": "Auto-merge: confidence 0.94",
    "timestamp": "2026-01-20T14:30:00Z"
  }
]

Response Fields

FieldTypeDescription
idUUIDEvent ID
actor_idUUIDWho performed the action
actor_typestring"user", "worker", "system", "api_key"
actionstringWhat happened (see table below)
resource_typestringResource type (e.g., "canonical_entity")
resource_idUUIDThe entity this event applies to
before_stateobjectEntity state before the change (null for creation)
after_stateobjectEntity state after the change
reasonstringHuman-readable explanation (null if not provided)
timestamptimestampWhen the event occurred

POST /v1/entities/:id/lock

Lock or unlock an entity. Locked entities are excluded from automatic merging during reconciliation. They can still be manually overridden.

Path Parameters

ParameterTypeRequiredDescription
idUUIDYesCanonical entity ID

Request Body

FieldTypeDefaultDescription
lockedbooleantrueSet to true to lock, false to unlock

Request

python
# Lock an entity
client.post("/v1/entities/550e8400-e29b-41d4-a716-446655440000/lock",
    json={"locked": True})

# Unlock an entity
client.post("/v1/entities/550e8400-e29b-41d4-a716-446655440000/lock",
    json={"locked": False})
bash
# Lock
curl -X POST "https://api.kanoniv.com/v1/entities/550e8400-e29b-41d4-a716-446655440000/lock" \
  -H "X-API-Key: kn_..." \
  -H "Content-Type: application/json" \
  -d '{"locked": true}'

# Unlock
curl -X POST "https://api.kanoniv.com/v1/entities/550e8400-e29b-41d4-a716-446655440000/lock" \
  -H "X-API-Key: kn_..." \
  -H "Content-Type: application/json" \
  -d '{"locked": false}'

Response

Returns 200 OK with no body on success. Returns 404 if the entity does not exist.


POST /v1/entities/:id/revert/:event_id

Revert an entity's canonical data to the state captured in a specific audit event. The revert restores the after_state from the target event as the current canonical data.

Path Parameters

ParameterTypeRequiredDescription
idUUIDYesCanonical entity ID
event_idUUIDYesAudit event ID to restore state from

Request

python
# Get history first to find the event to revert to
history = client.get("/v1/entities/550e8400-e29b-41d4-a716-446655440000/history")
target_event = history[1]["id"]  # Revert to second-most-recent state

reverted = client.post(
    f"/v1/entities/550e8400-e29b-41d4-a716-446655440000/revert/{target_event}")
print(reverted["canonical_data"])
bash
curl -X POST "https://api.kanoniv.com/v1/entities/550e8400-e29b-41d4-a716-446655440000/revert/evt_002" \
  -H "X-API-Key: kn_..."

Response

Returns the updated CanonicalEntity with the restored data:

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "tenant_id": "t_abc123",
  "entity_type": "customer",
  "canonical_data": {
    "email": "[email protected]",
    "name": "John Doe"
  },
  "field_provenance": { "email": "Stripe", "name": "Salesforce" },
  "confidence_score": 0.94,
  "is_locked": false,
  "created_at": "2026-01-15T10:00:00Z",
  "updated_at": "2026-02-05T10:30:00Z"
}

Returns 404 if the entity or audit event does not exist. Returns 400 if the audit event has no state to restore.

WARNING

The revert restores the after_state from the target audit event as the entity's current canonical_data. Make sure you select the correct event. Check its after_state in the history response before reverting.

The identity and delegation layer for AI agents.