Extensible Identity Checks in Go with Didit's Primitives
Discover how to design a flexible plugin architecture in Go for custom identity checks, leveraging Didit's modular identity primitives. This approach allows for scalable, adaptable verification workflows that can easily.

Modular Design for AgilityBuilding an extensible plugin architecture in Go allows businesses to quickly adapt to evolving compliance regulations and new fraud vectors without overhauling their entire identity verification system.
Leverage Go's StrengthsGo's strong typing, performance, and concurrency features make it an ideal language for developing robust and scalable identity verification services capable of handling high throughput.
Composability with Didit PrimitivesIntegrating Didit's AI-native identity primitives, such as ID Verification, Passive & Active Liveness, and AML Screening, provides powerful, pre-built components that can be orchestrated within custom Go plugins.
Didit's Developer-First ApproachDidit's clean APIs, instant sandbox, and modular architecture are perfectly suited for developers building extensible systems, offering Free Core KYC and no setup fees to accelerate development.
The Need for Extensible Identity Verification
In today's rapidly changing digital landscape, identity verification (IDV) is no longer a static process. Regulations evolve, new fraud techniques emerge, and business needs shift. A rigid, monolithic IDV system quickly becomes a bottleneck, hindering innovation and increasing compliance risks. This is why an extensible plugin architecture is not just a luxury but a necessity, especially for developers working with performant languages like Go.
An extensible architecture allows businesses to add, remove, or modify identity checks dynamically without redeploying the entire service. This agility is crucial for adapting to region-specific KYC (Know Your Customer) requirements, integrating cutting-edge fraud detection mechanisms, or experimenting with new user onboarding flows. For example, a business might initially require basic ID Verification and Liveness Detection. Later, they might need to add Age Estimation for specific products or incorporate AML Screening for financial transactions. A plugin-based system handles these additions seamlessly.
Designing a Plugin Architecture in Go
Go's interface-driven design and strong typing make it an excellent choice for building robust plugin systems. The core idea is to define a common interface that all identity check "plugins" must implement. This interface acts as a contract, ensuring that any new check can be seamlessly integrated into the existing workflow.
Defining the Identity Check Interface
Let's consider a simple interface for an identity check:
package main
type IdentityCheckResult struct {
Passed bool
Details string
}
type IdentityChecker interface {
Name() string
Execute(data map[string]interface{}) (IdentityCheckResult, error)
}
Here, IdentityChecker defines two methods: Name() to identify the plugin and Execute() to perform the actual check, taking generic input data and returning a result. This abstraction allows for diverse checks, from document verification to biometric analysis.
Implementing Custom Checks with Didit's Primitives
Now, let's see how we can implement specific identity checks using Didit's powerful primitives within this plugin framework. Didit's modular architecture, clean APIs, and AI-native capabilities make it straightforward to integrate these advanced services.
Example: Didit ID Verification Plugin
For document-based identity verification, we can create a plugin that leverages Didit's ID Verification product, which includes OCR, MRZ, and barcode scanning across 220+ countries. This plugin would send document images and data to Didit's API and process the response.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type DiditIDVCheck struct{}
func (d *DiditIDVCheck) Name() string {
return "DiditIDVerification"
}
func (d *DiditIDVCheck) Execute(data map[string]interface{}) (IdentityCheckResult, error) {
documentImage, ok := data["documentImage"].(string)
if !ok {
return IdentityCheckResult{Passed: false, Details: "Missing document image"}, nil
}
// In a real application, you'd send the image to Didit's ID Verification API.
// For this example, we'll simulate a call.
// Documentation: https://docs.didit.me/core-technology/id-verification
// Simulate API call to Didit's ID Verification
// Replace with actual API call and error handling
fmt.Printf("Calling Didit ID Verification for image: %s...\n", documentImage[:20]) // Truncate for display
// Assume a successful response for demonstration
if documentImage != "invalid_id_image" {
return IdentityCheckResult{Passed: true, Details: "ID document verified by Didit."}, nil
} else {
return IdentityCheckResult{Passed: false, Details: "ID document verification failed by Didit." + " Invalid image provided."},
fmt.Errorf("Didit ID Verification failed")
}
}
Example: Didit Liveness Check Plugin
To combat deepfakes and presentation attacks, a liveness check is essential. Didit offers both Passive & Active Liveness detection. A plugin could integrate this, processing a short video or a series of images.
package main
type DiditLivenessCheck struct{}
func (d *DiditLivenessCheck) Name() string {
return "DiditLivenessDetection"
}
func (d *DiditLivenessCheck) Execute(data map[string]interface{}) (IdentityCheckResult, error) {
livenessVideo, ok := data["livenessVideo"].(string)
if !ok {
return IdentityCheckResult{Passed: false, Details: "Missing liveness video"}, nil
}
// Simulate API call to Didit's Liveness Detection
// Replace with actual API call and error handling
fmt.Printf("Calling Didit Liveness Detection for video: %s...\n", livenessVideo[:20])
if livenessVideo != "deepfake_video" {
return IdentityCheckResult{Passed: true, Details: "Liveness detected by Didit."}, nil
} else {
return IdentityCheckResult{Passed: false, Details: "Liveness detection failed by Didit." + " Deepfake detected."},
fmt.Errorf("Didit Liveness Detection failed")
}
}
Orchestrating Workflows with Plugins
Once you have your plugins, you need a mechanism to orchestrate them into a coherent workflow. Didit's Orchestrated Workflows provide a no-code visual builder for this, but if you're building a custom Go service, you can manage this logic programmatically.
package main
import "fmt"
// A simple orchestrator
type Workflow struct {
Checks []IdentityChecker
}
func (w *Workflow) AddCheck(checker IdentityChecker) {
w.Checks = append(w.Checks, checker)
}
func (w *Workflow) Run(userData map[string]interface{}) ([]IdentityCheckResult, error) {
var results []IdentityCheckResult
for _, check := range w.Checks {
fmt.Printf("\nRunning %s check...\n", check.Name())
res, err := check.Execute(userData)
results = append(results, res)
if err != nil || !res.Passed {
fmt.Printf("Check %s failed: %s\n", check.Name(), res.Details)
return results, fmt.Errorf("workflow failed at %s: %w", check.Name(), err)
}
fmt.Printf("Check %s passed: %s\n", check.Name(), res.Details)
}
return results, nil
}
func main() {
workflow := &Workflow{}
workflow.AddCheck(&DiditIDVCheck{})
workflow.AddCheck(&DiditLivenessCheck{})
// Example user data
userData := map[string]interface{}{
"documentImage": "valid_passport_image_base64",
"livenessVideo": "valid_liveness_video_base64",
"name": "Jane Doe",
}
fmt.Println("--- Running successful workflow ---")
results, err := workflow.Run(userData)
if err != nil {
fmt.Printf("Workflow completed with errors: %v\n", err)
} else {
fmt.Println("Workflow completed successfully!")
}
for _, r := range results {
fmt.Printf("- %s: Passed = %t, Details = %s\n", r.Name, r.Passed, r.Details)
}
// Example of a failing workflow
fmt.Println("\n--- Running failing workflow (invalid ID) ---")
failingUserData := map[string]interface{}{
"documentImage": "invalid_id_image",
"livenessVideo": "valid_liveness_video_base64",
"name": "John Smith",
}
failingResults, err := workflow.Run(failingUserData)
if err != nil {
fmt.Printf("Workflow completed with errors: %v\n", err)
} else {
fmt.Println("Workflow completed successfully!")
}
for _, r := range failingResults {
fmt.Printf("- %s: Passed = %t, Details = %s\n", r.Name, r.Passed, r.Details)
}
}
This simple orchestrator executes checks sequentially. In a real-world scenario, you might add conditional logic, parallel execution, or retries, mirroring the capabilities of Didit's no-code workflow builder.
Advanced Considerations for Go Plugin Architectures
When building a production-ready plugin architecture in Go, consider these aspects:
- Error Handling: Implement robust error handling and logging for each plugin.
- Configuration: Allow plugins to be configured dynamically (e.g., API keys, thresholds).
- Plugin Loading: For true extensibility without recompilation, explore Go's
pluginpackage (though it has platform limitations) or external processes/gRPC for inter-process communication. - Security: Ensure that plugins are sandboxed or properly vetted to prevent malicious code execution.
- Data Flow: Define clear data contracts for inputs and outputs of each plugin.
- White-Labeling: If you're building a user-facing flow, remember that Didit offers extensive white-labeling options, allowing you to customize colors, fonts, logos, and even use a custom domain, ensuring a seamless brand experience. This is crucial for maintaining user trust and reducing drop-off rates during the verification process.
How Didit Helps
Didit is designed from the ground up for exactly this kind of modular, extensible integration. As an AI-native, developer-first identity platform, Didit provides the building blocks—or "primitives"—that can be seamlessly integrated into your Go plugin architecture. Our modular architecture means you can pick and choose the exact verification components you need, whether it's ID Verification, Passive & Active Liveness, 1:1 Face Match, AML Screening & Monitoring, Proof of Address, or Age Estimation. Each of these can be a distinct plugin in your system.
Didit stands out by offering Free Core KYC, allowing you to get started with essential identity checks without upfront costs. Our clean APIs and comprehensive documentation, alongside an instant sandbox, make integration intuitive and fast. By leveraging Didit, you offload the complexity of maintaining sophisticated AI models, global document databases, and compliance updates, allowing your Go service to focus on orchestration and business logic. Didit's pay-per-successful-check model and absence of setup fees further align with a flexible, scalable architecture, ensuring you only pay for what you use.
Ready to Get Started?
Ready to see Didit in action? Get a free demo today.
Start verifying identities for free with Didit's free tier.