Skip to content

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-go

Quick 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:

FieldTypePurpose
client.Entities*EntitiesServiceSearch, get, link, lock, revert entities
client.Sources*SourcesServiceCRUD data sources, sync, preview, mappings
client.ResolveRt*ResolveServiceReal-time and bulk identity resolution
client.Jobs*JobsServiceList, run, cancel reconciliation jobs
client.Rules*RulesServiceList, create, view history of matching rules
client.Reviews*ReviewsServiceList and decide on pending match reviews
client.Overrides*OverridesServiceForce merge or split via manual overrides
client.Audit*AuditServiceView audit trail and entity event history
client.Specs*SpecsServiceList, get, upload identity specs
client.Feedback*FeedbackServiceActive learning feedback labels
client.Webhooks*WebhooksServiceOutbound webhook management
client.Sandbox*SandboxServiceSandbox 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.Results

Webhooks

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.EntitiesDeleted

Error 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)
        }
    }
}
SentinelHTTP statusWhen
ErrValidation400Invalid request body
ErrAuthentication401Missing or invalid credentials
ErrForbidden403Insufficient permissions
ErrNotFound404Resource not found
ErrConflict409Duplicate resource
ErrRateLimit429Rate or quota limit hit
ErrServer5xxServer-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

The identity and delegation layer for AI agents.