Official Go client for the Email Funnel AI integration API. Connect with your project key and secret, then call typed, service-oriented methods — no manual URLs, headers, or JSON plumbing.
- Standard library only (
net/http+encoding/json) — zero dependencies - Idiomatic services:
client.Contacts.Sync(ctx, req)returning(Data, error) - Typed error model (
*APIError) with status,error_type, and 422 field messages - Injectable transport (
HTTPClient) —*http.Clientsatisfies it directly - Full coverage of all 26 endpoints
go get github.com/emailfunnelai/sdk-goimport emailfunnelai "github.com/emailfunnelai/sdk-go"package main
import (
"context"
"fmt"
"log"
emailfunnelai "github.com/emailfunnelai/sdk-go"
)
func main() {
ctx := context.Background()
// Base URL defaults to https://app.emailfunnel.ai — override only for staging/local.
client := emailfunnelai.New("pk_your_project_key", "sk_your_secret_key")
// 1. Confirm your credentials
if _, err := client.Validate(ctx); err != nil {
log.Fatal(err)
}
// 2. Get or create a list
list, err := client.Lists.Create(ctx, emailfunnelai.ListCreateRequest{Name: "Newsletter signups"})
if err != nil {
log.Fatal(err)
}
var created struct {
ID string `json:"id"`
}
if err := list.Into(&created); err != nil {
log.Fatal(err)
}
// 3. Sync a contact into it
_, err = client.Contacts.Sync(ctx, emailfunnelai.ContactSyncRequest{
Contact: map[string]any{"email": "jane@example.com", "source_type": "custom_saas"},
ContactListID: created.ID,
})
if err != nil {
log.Fatal(err)
}
fmt.Println("done")
}Every call returns the unwrapped data payload as a Data value. Decode it into
your own type with data.Into(&v), or read the raw JSON with data.String().
Every group is reached from the client as client.<Service>.<Method>(ctx, …).
// Single contact
client.Contacts.Sync(ctx, emailfunnelai.ContactSyncRequest{
Contact: map[string]any{"email": "jane@example.com", "source_type": "app"},
ContactListID: "42",
})
// Bulk (up to 500; the server queues automatically above 100 rows)
client.Contacts.Bulk(ctx, emailfunnelai.BulkSyncRequest{
Contacts: []map[string]any{{"email": "a@example.com"}},
ContactListID: "42",
SourceType: "import",
Async: true, // force queued processing
})
// Real-time webhook event
client.Contacts.Webhook(ctx, emailfunnelai.WebhookRequest{
Event: "order.completed",
Data: map[string]any{"email": "a@example.com"},
ContactListID: "42",
})client.Lists.All(ctx)
client.Lists.Create(ctx, emailfunnelai.ListCreateRequest{Name: "VIP", Description: "High-value buyers"})
client.Lists.Find(ctx, "42")client.Bindings.All(ctx)
client.Bindings.Create(ctx, emailfunnelai.BindingCreateRequest{SourceType: "custom_crm", ContactListID: "42"})
client.Bindings.Find(ctx, "5")
client.Bindings.Update(ctx, "5", emailfunnelai.BindingUpdateRequest{SyncEnabled: emailfunnelai.Bool(false)})
client.Bindings.Delete(ctx, "5")
client.Bindings.Status(ctx, "5", emailfunnelai.BindingStatusRequest{Status: "completed", ErrorsCount: emailfunnelai.Int(0)})client.FieldMappings.Config(ctx, "custom_saas")
client.FieldMappings.ForBinding("5").Get(ctx)
client.FieldMappings.ForBinding("5").Update(ctx, map[string]string{"email": "Email"})
client.FieldMappings.ForBinding("5").Reset(ctx)client.AutoTagging.Rules(ctx, "custom_saas")
client.AutoTagging.Preview(ctx, "custom_saas", map[string]any{"plan": "pro"})client.Analytics.Dashboard(ctx)
client.Analytics.Heatmap(ctx, emailfunnelai.HeatmapOptions{Range: 30, EmailType: "campaign", Timezone: "America/New_York"})
client.Analytics.Funnels(ctx)
client.Analytics.Campaigns(ctx)
client.Analytics.Forms(ctx)client.SSO.Generate(ctx, "admin@example.com")
client.SSO.TeamMembers(ctx)Successful calls return the unwrapped data payload. Any error response returns
an *APIError; use AsAPIError to inspect it:
data, err := client.Contacts.Sync(ctx, req)
if err != nil {
if apiErr, ok := emailfunnelai.AsAPIError(err); ok {
apiErr.Status // 422
apiErr.ErrorType // "validation_error"
apiErr.Messages // map[string][]string — set on 422
apiErr.RetryAfter // int seconds — set on 429 rate limits
}
// otherwise a transport/decoding error
}Branch on ErrorType for specific conditions — e.g. a contact list that has
been deactivated rejects new members with a 409 list_inactive:
if _, err := client.Contacts.Sync(ctx, req); err != nil {
if apiErr, ok := emailfunnelai.AsAPIError(err); ok && apiErr.ErrorType == "list_inactive" {
// The target list is inactive — reactivate it or pick another list.
}
}error_type |
Status | Meaning |
|---|---|---|
invalid_credentials |
401 | Missing/invalid project key or secret |
inactive_project |
403 | The connected project is inactive |
validation_error |
422 | Request body failed validation (Messages set) |
invalid_email / suppressed |
422 | Email is undeliverable or suppressed |
list_inactive |
409 | Target contact list is inactive and rejects new members |
rate_limit_exceeded |
429 | 1000 req/hour cap hit (RetryAfter set) |
sync_failed |
500 | Unexpected sync failure |
import "net/http"
// Point at a local dev server
client := emailfunnelai.New("pk", "sk", emailfunnelai.WithBaseURL("http://localhost:8000"))
// Inject a pre-configured *http.Client (timeouts, proxies, transport)
httpClient := &http.Client{Timeout: 30 * time.Second}
client = emailfunnelai.New("pk", "sk", emailfunnelai.WithHTTPClient(httpClient))Any type implementing HTTPClient (a single Do(*http.Request) (*http.Response, error)
method) can be injected — used by this package's own test suite for fast,
offline tests.
go test ./...MIT © Email Funnel AI. Authored by Email Funnel AI and Ratul Hasan.