Admin API
User management, API keys, tenant settings, jobs, observability, billing, and dashboard stats.
Admin Only
User management, API key management, and tenant settings require the admin role. Requests from data or viewer roles will receive a 403 Forbidden response.
User Management
GET /v1/users
List all users in the current tenant.
import httpx
resp = httpx.get("https://api.kanoniv.com/v1/users",
headers={"X-API-Key": "kn_..."})
users = resp.json()
for user in users:
print(f"{user['email']} - role: {user['role']}")curl "https://api.kanoniv.com/v1/users" \
-H "X-API-Key: kn_..."Response
Returns an array of UserResponse objects:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "t_abc123",
"external_id": null,
"provisioning_source": null,
"email": "[email protected]",
"name": "Admin User",
"role": "admin",
"must_change_password": false,
"is_verified": true,
"created_at": "2026-01-01T00:00:00Z"
}
]UserResponse Fields
| Field | Type | Description |
|---|---|---|
id | UUID | User ID |
tenant_id | UUID | Tenant this user belongs to |
external_id | string | External identity provider ID (null for local users) |
provisioning_source | string | SSO/SCIM provisioning source (null for local users) |
email | string | Email address |
name | string | Display name |
role | string | One of: admin, data, viewer |
must_change_password | boolean | Whether the user must change their password on next login |
is_verified | boolean | Whether the email has been verified |
created_at | timestamp | When the user was created |
POST /v1/users
Create a new user in the current tenant. Admin-created users are automatically verified.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User email address (max 255 characters) |
name | string | Yes | Display name (1--100 characters) |
role | string | Yes | One of: admin, data, viewer |
password | string | Yes | Initial password (8--128 characters) |
curl -X POST "https://api.kanoniv.com/v1/users" \
-H "X-API-Key: kn_..." \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"name": "Data Analyst",
"role": "data",
"password": "secure-password-123"
}'Response
Returns 201 Created with a UserResponse object.
PUT /v1/users/:id
Update a user's role.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
role | string | Yes | New role: admin, data, or viewer |
curl -X PUT "https://api.kanoniv.com/v1/users/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: kn_..." \
-H "Content-Type: application/json" \
-d '{"role": "admin"}'Response
Returns 200 OK with the updated UserResponse.
DELETE /v1/users/:id
Delete a user. You cannot delete yourself.
curl -X DELETE "https://api.kanoniv.com/v1/users/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: kn_..."Response
Returns 204 No Content on success.
API Key Management
GET /v1/api-keys
List all API keys for the current tenant.
curl "https://api.kanoniv.com/v1/api-keys" \
-H "X-API-Key: kn_..."Response
Returns an array of ApiKey objects:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "t_abc123",
"name": "Production Integration",
"key_hash": "a1b2c3d4...",
"key_prefix": "kn_abc12",
"scopes": ["data"],
"expires_at": null,
"last_used_at": "2026-02-05T09:45:00Z",
"created_at": "2026-01-20T10:00:00Z"
}
]ApiKey Fields
| Field | Type | Description |
|---|---|---|
id | UUID | Key ID |
tenant_id | UUID | Tenant this key belongs to |
name | string | Human-readable key name |
key_hash | string | SHA-256 hash of the key (for verification) |
key_prefix | string | First 8 characters of the key (for identification) |
scopes | JSON | Permission scopes (e.g., ["admin"], ["data"], ["ingest"], or [] for full access) |
expires_at | timestamp | Key expiration (null for non-expiring keys) |
last_used_at | timestamp | Last time the key was used (null if never used) |
created_at | timestamp | When the key was created |
POST /v1/api-keys
Create a new API key. The full key value is returned only once in the response.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable key name (1--200 characters) |
expires_at | timestamp | No | Optional expiration date |
scopes | JSON | No | Permission scopes (defaults to [] for full access) |
curl -X POST "https://api.kanoniv.com/v1/api-keys" \
-H "X-API-Key: kn_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Data Pipeline",
"scopes": ["data"]
}'Response
Returns 201 Created:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Data Pipeline",
"api_key": "kn_a1b2c3d4e5f6...",
"expires_at": null
}Save your API key
The full API key value is only returned once at creation time. Store it securely. If lost, revoke the key and create a new one.
DELETE /v1/api-keys/:id
Revoke an API key. Requires admin role.
curl -X DELETE "https://api.kanoniv.com/v1/api-keys/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: kn_..."Response
Returns 204 No Content on success.
Tenant Settings
PATCH /v1/tenant
Update tenant name and settings. Settings are merged; existing keys not included in the request are preserved.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Updated tenant name |
settings | object | No | Settings to merge into current tenant settings |
curl -X PATCH "https://api.kanoniv.com/v1/tenant" \
-H "X-API-Key: kn_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"settings": {
"default_entity_type": "customer"
}
}'Response
Returns 200 OK with the full Tenant object.
Reconciliation Settings
GET /v1/settings/reconciliation
Get reconciliation threshold settings. These are stored in the tenant's settings.reconciliation JSON path.
curl "https://api.kanoniv.com/v1/settings/reconciliation" \
-H "X-API-Key: kn_..."Response
{
"merge_threshold": 0.85,
"review_threshold": 0.60
}| Field | Type | Description |
|---|---|---|
merge_threshold | float | Score above which entities are auto-merged |
review_threshold | float | Score above which entities are queued for review |
PATCH /v1/settings/reconciliation
Update reconciliation thresholds.
curl -X PATCH "https://api.kanoniv.com/v1/settings/reconciliation" \
-H "X-API-Key: kn_..." \
-H "Content-Type: application/json" \
-d '{
"merge_threshold": 0.90,
"review_threshold": 0.70
}'Response
Returns 200 OK with the updated ReconciliationSettings.
Survivorship Policies
GET /v1/settings/survivorship
List survivorship policies that control field-level source prioritization.
curl "https://api.kanoniv.com/v1/settings/survivorship" \
-H "X-API-Key: kn_..."Response
Returns an array of SurvivorshipPolicy objects:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "t_abc123",
"field_name": "email",
"priority_config": {
"strategy": "source_priority",
"sources": ["Salesforce", "Stripe", "HubSpot"]
},
"is_active": true,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-02-01T09:00:00Z"
}
]| Field | Type | Description |
|---|---|---|
id | UUID | Policy ID |
tenant_id | UUID | Tenant this policy belongs to |
field_name | string | The canonical field this policy applies to |
priority_config | object | Free-form JSON with strategy and prioritization rules |
is_active | boolean | Whether this policy is active |
created_at | timestamp | When the policy was created |
updated_at | timestamp | When the policy was last updated |
POST /v1/settings/survivorship
Create or update a survivorship policy. If the id is a nil UUID or omitted, a new policy is created.
curl -X POST "https://api.kanoniv.com/v1/settings/survivorship" \
-H "X-API-Key: kn_..." \
-H "Content-Type: application/json" \
-d '{
"field_name": "email",
"priority_config": {
"strategy": "source_priority",
"sources": ["Salesforce", "Stripe"]
},
"is_active": true
}'Response
Returns 200 OK with the upserted SurvivorshipPolicy.
DELETE /v1/settings/survivorship/:id
Delete a survivorship policy. Returns 204 No Content.
Audit Streams
GET /v1/settings/audit-streams
List configured audit log streaming destinations.
Response
Returns an array of AuditStream objects:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "t_abc123",
"destination_type": "webhook",
"destination_url": "https://logs.company.com/audit",
"auth_config": { "header": "X-Auth-Token", "value": "secret" },
"is_active": true,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-02-01T09:00:00Z"
}
]POST /v1/settings/audit-streams
Create a new audit stream. Returns 201 Created with the AuditStream.
PUT /v1/settings/audit-streams/:id
Update an existing audit stream. Returns 200 OK with the updated AuditStream.
DELETE /v1/settings/audit-streams/:id
Delete an audit stream. Returns 204 No Content.
Retention Policies
GET /v1/settings/retention-policies
List data retention policies.
Response
Returns an array of RetentionPolicy objects:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "t_abc123",
"entity_type": "customer",
"retention_days": 365,
"shred_pii": true,
"is_active": true,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-02-01T09:00:00Z"
}
]| Field | Type | Description |
|---|---|---|
entity_type | string | Entity type this policy applies to |
retention_days | integer | Days to retain data before cleanup |
shred_pii | boolean | Whether to shred PII fields on cleanup |
is_active | boolean | Whether this policy is active |
POST /v1/settings/retention-policies
Create or update a retention policy. Returns 200 OK with the RetentionPolicy.
DELETE /v1/settings/retention-policies/:id
Delete a retention policy. Returns 204 No Content.
SSO Configuration
GET /v1/settings/sso-config
List SSO configurations for the tenant.
Response
Returns an array of SsoConfig objects:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "t_abc123",
"provider_type": "oidc",
"display_name": "Company SSO",
"issuer_url": "https://accounts.google.com",
"client_id": "123456.apps.googleusercontent.com",
"client_secret_enc": "encrypted...",
"auth_url": "https://accounts.google.com/o/oauth2/auth",
"token_url": "https://oauth2.googleapis.com/token",
"jwks_url": "https://www.googleapis.com/oauth2/v3/certs",
"saml_metadata_url": null,
"is_active": true,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-02-01T09:00:00Z"
}
]POST /v1/settings/sso-config
Create or update an SSO configuration. If id is nil, a new config is created; otherwise the existing config is updated. Returns 200 OK with the SsoConfig.
DELETE /v1/settings/sso-config/:id
Delete an SSO configuration. Returns 204 No Content.
Jobs
GET /v1/jobs
List reconciliation jobs for your tenant.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Max results (capped at 200) |
job_type | string | N/A | Filter by job type |
curl "https://api.kanoniv.com/v1/jobs?limit=20" \
-H "X-API-Key: kn_..."Response
Returns an array of BatchRun objects:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "t_abc123",
"status": "completed",
"job_type": "reconciliation",
"stats": { "entities_processed": 5000, "merges": 120, "splits": 5 },
"started_at": "2026-02-05T09:00:00Z",
"completed_at": "2026-02-05T09:05:00Z",
"triggered_by": "660e8400-e29b-41d4-a716-446655440001",
"created_at": "2026-02-05T09:00:00Z"
}
]BatchRun Fields
| Field | Type | Description |
|---|---|---|
id | UUID | Job ID |
tenant_id | UUID | Tenant this job belongs to |
status | string | Job status (e.g., "pending", "running", "completed", "failed", "cancelled") |
job_type | string | Job type (e.g., "reconciliation") |
stats | object | Free-form JSON with execution statistics |
started_at | timestamp | When the job started (null if pending) |
completed_at | timestamp | When the job completed (null if still running) |
triggered_by | UUID | User ID who triggered the job (null for scheduled jobs) |
created_at | timestamp | When the job was created |
POST /v1/jobs/run
Trigger a new reconciliation job. The job is queued and picked up by a worker.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
job_type | string | No | Job type (defaults to "reconciliation", max 50 characters) |
payload | object | No | Free-form JSON payload for the job |
curl -X POST "https://api.kanoniv.com/v1/jobs/run" \
-H "X-API-Key: kn_..." \
-H "Content-Type: application/json" \
-d '{}'Response
Returns 202 Accepted:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending"
}GET /v1/jobs/:id
Get details for a specific job. Returns 404 if not found.
Response
Returns a BatchRun object (same schema as list response).
POST /v1/jobs/:id/cancel
Cancel a pending or locked job. Returns 200 OK on success, 404 if the job is not found or not in a cancellable state.
Observability
GET /v1/identity/specs/:version/summary
Get a static plan summary for a compiled identity spec. Returns analysis of rules, risk flags, and coverage.
curl "https://api.kanoniv.com/v1/identity/specs/customer-v3/summary" \
-H "X-API-Key: kn_..."Response
Returns a PlanSummary object with rule counts, risk analysis, and coverage breakdown.
GET /v1/jobs/:id/summary
Get the identity summary from a completed reconciliation job. Returns 400 if the job has not completed.
Response
Returns an IdentitySummary extracted from the job's stats.identity_summary.
GET /v1/jobs/:id/health
Get health metrics from a completed reconciliation job. Returns 400 if the job has not completed.
Response
Returns a RunHealth object extracted from the job's stats.run_health.
GET /v1/jobs/:id/telemetry
Get per-rule telemetry data from a completed reconciliation job. Returns 400 if the job has not completed.
Response
{
"rules": [
{
"rule_name": "email_exact",
"matches": 1200,
"avg_score": 0.95,
"p50_latency_ms": 2,
"p99_latency_ms": 15
}
]
}GET /v1/jobs/:id/diff
Compare two reconciliation runs. Requires baseline_id query parameter. Currently a placeholder; returns a message indicating the feature is coming in a future release.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
baseline_id | UUID | Yes | The baseline job ID to compare against |
Billing
POST /v1/billing/checkout
Create a Stripe checkout session for subscribing to a plan.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
plan | string | Yes | Plan name: "starter" or "growth" |
curl -X POST "https://api.kanoniv.com/v1/billing/checkout" \
-H "X-API-Key: kn_..." \
-H "Content-Type: application/json" \
-d '{"plan": "starter"}'Response
{
"checkout_url": "https://checkout.stripe.com/c/pay/cs_..."
}Redirect the user to checkout_url to complete payment. Cloud plans require contacting sales.
POST /v1/billing/portal
Create a Stripe billing portal session for managing subscriptions, invoices, and payment methods.
curl -X POST "https://api.kanoniv.com/v1/billing/portal" \
-H "X-API-Key: kn_..."Response
{
"portal_url": "https://billing.stripe.com/p/session/..."
}Returns 400 if no billing account exists (subscribe first).
GET /v1/billing/status
Get current billing status and usage.
curl "https://api.kanoniv.com/v1/billing/status" \
-H "X-API-Key: kn_..."Response
{
"plan": "starter",
"billing_status": "active",
"identity_count": 45000,
"included_limit": 100000,
"overage": 0,
"is_trial": false
}| Field | Type | Description |
|---|---|---|
plan | string | Current tier: "local", or "cloud" |
billing_status | string | Billing state (e.g., "active", "past_due", "canceled") |
identity_count | integer | Current number of external entities |
included_limit | integer | Entities included in the plan (Local: unlimited local, Cloud: per agreement) |
overage | integer | Entities exceeding the included limit |
is_trial | boolean | Whether the tenant is on a trial (no billing account) |
Dashboard Stats
GET /v1/stats
Get dashboard statistics for the current tenant.
curl "https://api.kanoniv.com/v1/stats" \
-H "X-API-Key: kn_..."Response
{
"total_canonical_entities": 12500,
"total_external_entities": 45000,
"pending_reviews": 23,
"merge_rate": 0.87
}| Field | Type | Description |
|---|---|---|
total_canonical_entities | integer | Total canonical entities for the tenant |
total_external_entities | integer | Total external entities for the tenant |
pending_reviews | integer | Entity pairs awaiting manual review |
merge_rate | float | Ratio of merge decisions to total match results |
Roles Reference
| Role | Users | API Keys | Settings | Sources | Rules | Overrides | Jobs |
|---|---|---|---|---|---|---|---|
admin | Full | Full | Full | Full | Full | Full | Full |
data | N/A | N/A | N/A | Read/Write | Read/Write | Read/Write | Read/Write |
viewer | N/A | N/A | N/A | Read | Read | Read | Read |
