Go SDK
The kanoniv-go package is a zero-dependency Go client for the Kanoniv Cloud API. It uses only net/http and encoding/json from the standard library.
Installation
bash
go get github.com/kanoniv/kanoniv-goQuick start
go
package main
import (
"context"
"fmt"
kanoniv "github.com/kanoniv/kanoniv-go"
)
func main() {
client := kanoniv.NewClient(kanoniv.WithAPIKey("kn_..."))
defer client.Close()
ctx := context.Background()
// Resolve an identity
entity, err := client.Resolve(ctx, &kanoniv.ResolveParams{
System: "crm", ExternalID: "sf_123",
})
// Real-time resolve
result, err := client.ResolveRt.Realtime(ctx, &kanoniv.RealtimeResolveParams{
SourceName: "crm",
ExternalID: "cus_456",
Data: map[string]interface{}{"email": "[email protected]", "name": "Jane Doe"},
})
// Batch ingest
_, err = client.Ingest(ctx, &kanoniv.IngestParams{
SourceName: "crm",
Records: []map[string]interface{}{
{"external_id": "1", "email": "[email protected]"},
{"external_id": "2", "email": "[email protected]"},
},
})
}Configuration
go
client := kanoniv.NewClient(
kanoniv.WithAPIKey("kn_..."), // API key authentication
// kanoniv.WithAccessToken("jwt_..."), // Or JWT bearer token
kanoniv.WithBaseURL("https://api.kanoniv.com"), // Default
kanoniv.WithTimeout(30 * time.Second), // Default: 30s
kanoniv.WithMaxRetries(2), // Default: 2
kanoniv.WithHTTPClient(customClient), // Optional custom http.Client
)
defer client.Close()Services
The client exposes 12 services matching the API surface:
| Field | Type | Purpose |
|---|---|---|
client.Entities | *EntitiesService | Search, get, link, lock, revert entities |
client.Sources | *SourcesService | CRUD data sources, sync, preview, mappings |
client.ResolveRt | *ResolveService | Real-time and bulk identity resolution |
client.Jobs | *JobsService | List, run, cancel reconciliation jobs |
client.Rules | *RulesService | List, create, view history of matching rules |
client.Reviews | *ReviewsService | List and decide on pending match reviews |
client.Overrides | *OverridesService | Force merge or split via manual overrides |
client.Audit | *AuditService | View audit trail and entity event history |
client.Specs | *SpecsService | List, get, upload identity specs |
client.Feedback | *FeedbackService | Active learning feedback labels |
client.Webhooks | *WebhooksService | Outbound webhook management |
client.Sandbox | *SandboxService | Sandbox status and reset |
Every method takes context.Context as its first parameter.
Top-level methods
go
// Resolve by source + ID or free-text query
result, err := client.Resolve(ctx, &kanoniv.ResolveParams{System: "crm", ExternalID: "sf_123"})
result, err := client.Resolve(ctx, &kanoniv.ResolveParams{Query: "[email protected]"})
// Batch ingest records
result, err := client.Ingest(ctx, &kanoniv.IngestParams{
SourceName: "crm",
Records: records,
EntityType: "person",
})
// Dashboard statistics
stats, err := client.Stats(ctx)Entities
go
// Search entities
results, err := client.Entities.Search(ctx, &kanoniv.EntitySearchParams{
Q: "jane", EntityType: "person", Limit: 20,
})
// Get canonical entity
entity, err := client.Entities.Get(ctx, "entity-uuid")
// Get entity with linked records
linked, err := client.Entities.GetLinked(ctx, "entity-uuid")
// Bulk linked entities (max 1000)
bulk, err := client.Entities.GetLinkedBulk(ctx, []string{"id1", "id2"})
// Entity history, lock, revert
history, err := client.Entities.History(ctx, "entity-uuid")
_, err = client.Entities.Lock(ctx, "entity-uuid")
_, err = client.Entities.Revert(ctx, "entity-uuid", "event-uuid")Real-time resolution
go
// Single record resolve
result, err := client.ResolveRt.Realtime(ctx, &kanoniv.RealtimeResolveParams{
SourceName: "crm",
ExternalID: "cus_123",
Data: map[string]interface{}{"email": "[email protected]", "name": "Jane"},
})
// result.EntityID, result.IsNew, result.Confidence
// Bulk resolve (max 1000 lookups)
bulk, err := client.ResolveRt.Bulk(ctx, []kanoniv.BulkLookup{
{Source: "crm", ID: "cus_123"},
{Source: "erp", ID: "emp_456"},
})
// bulk.Resolved, bulk.NotFound, bulk.ResultsWebhooks
go
// Create (signing secret returned only on creation)
webhook, err := client.Webhooks.Create(ctx, &kanoniv.CreateWebhookParams{
URL: "https://your-server.com/hook",
EventTypes: []string{"entity.merged", "reconciliation.completed"},
})
// List, get, delete, test
all, err := client.Webhooks.List(ctx)
one, err := client.Webhooks.Get(ctx, "wh-id")
err = client.Webhooks.Delete(ctx, "wh-id")
result, err := client.Webhooks.Test(ctx, "wh-id")
// Delivery history
deliveries, err := client.Webhooks.Deliveries(ctx, "wh-id", &kanoniv.DeliveryListParams{Limit: 50})Sandbox
go
status, err := client.Sandbox.Status(ctx)
// status.IsSandbox, status.EntityCount, status.EntityLimit, status.SourceCount
result, err := client.Sandbox.Reset(ctx)
// result.Message, result.EntitiesDeletedError handling
All API errors are returned as *KanonivError. Use errors.Is() with sentinel errors for specific handling:
go
import "errors"
_, err := client.Entities.Get(ctx, "nonexistent")
if err != nil {
var apiErr *kanoniv.KanonivError
if errors.As(err, &apiErr) {
switch {
case errors.Is(err, kanoniv.ErrNotFound):
fmt.Println("Entity not found")
case errors.Is(err, kanoniv.ErrRateLimit):
fmt.Printf("Retry after %.0fs\n", apiErr.RateLimitInfo())
case errors.Is(err, kanoniv.ErrServer):
fmt.Println("Server error - retry with backoff")
default:
fmt.Printf("API error %d: %s\n", apiErr.StatusCode, apiErr.Message)
}
}
}| Sentinel | HTTP status | When |
|---|---|---|
ErrValidation | 400 | Invalid request body |
ErrAuthentication | 401 | Missing or invalid credentials |
ErrForbidden | 403 | Insufficient permissions |
ErrNotFound | 404 | Resource not found |
ErrConflict | 409 | Duplicate resource |
ErrRateLimit | 429 | Rate or quota limit hit |
ErrServer | 5xx | Server-side issue |
Automatic retries
The SDK automatically retries on transient errors (408, 429, 502, 503, 504) with exponential backoff. Rate limit responses honor the Retry-After header. Configure with WithMaxRetries() (default: 2).
Requirements
- Go 1.21+
- Zero external dependencies
