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.