Go SDK
Integrate Perly churn prevention into your Go application with standard net/http middleware and a typed user builder.
github.com/perly-ai/perly-gov1.0.05 minutes
Installation
go get github.com/perly-ai/perly-goUsage
Wrap your HTTP handler with the Perly middleware. The middleware intercepts every request and resolves the current user via the resolver function.
// main.go
package main
import (
"net/http"
"os"
perly "github.com/perly-ai/perly-go"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/", apiHandler)
handler := perly.Middleware(mux, perly.Config{
APIKey: os.Getenv("PERLY_API_KEY"),
Resolver: resolveUser,
})
http.ListenAndServe(":8080", handler)
}The SDK works with any router that satisfies http.Handler, including Chi, Gorilla Mux, and the standard library.
User Resolver
The resolver is a function that extracts the authenticated user from the request and returns a *perly.User built with the builder pattern.
func resolveUser(r *http.Request) *perly.User {
user := auth.UserFromContext(r.Context())
if user == nil {
return nil
}
return perly.NewBuilder().
SetID(user.ID).
SetMetadata(map[string]interface{}{
"plan": user.Plan,
"region": user.Region,
"company_id": user.CompanyID,
}).
LinkStripeByID(user.StripeCustomerID).
LinkHubspotByID(user.HubspotContactID).
Build()
}Tracking Events
Retrieve the Perly client from the request context to track customer engagement events.
func onboardingCompleteHandler(w http.ResponseWriter, r *http.Request) {
client := perly.ClientFromContext(r.Context())
user := auth.UserFromContext(r.Context())
err := client.Track(user.ID, "onboarding_completed", nil)
if err != nil {
http.Error(w, "tracking failed", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(map[string]bool{"success": true})
}
func exportReportHandler(w http.ResponseWriter, r *http.Request) {
client := perly.ClientFromContext(r.Context())
user := auth.UserFromContext(r.Context())
client.Track(user.ID, "report_exported", map[string]interface{}{
"format": r.URL.Query().Get("format"),
})
}Expansion Signals
Send signals when customers approach plan limits. Each signal includes relevant metadata for the expansion workflow.
func checkLimitsHandler(w http.ResponseWriter, r *http.Request) {
client := perly.ClientFromContext(r.Context())
user := auth.UserFromContext(r.Context())
seats := getSeatCount(user.CompanyID)
if seats.Current > int(float64(seats.Limit) * 0.9) {
client.Signal(user.ID, "seat_limit_near", map[string]interface{}{
"current": seats.Current,
"limit": seats.Limit,
})
}
}
// Other signal types
client.Signal(userID, "api_usage_high", map[string]interface{}{"current": 9500, "limit": 10000})
client.Signal(userID, "rate_limit_hit", map[string]interface{}{"endpoint": "/api/search"})
client.Signal(userID, "storage_limit_near", map[string]interface{}{"used_gb": 9.2, "limit_gb": 10})