Go client library for the Lunogram Client API. Manage user profiles, organizations, events, and scheduled resources from any Go application.
go get github.com/lunogram/go-sdkRequires Go 1.22 or later.
package main
import (
"context"
"fmt"
"log"
lunogram "github.com/lunogram/go-sdk"
)
func main() {
client := lunogram.NewClient("your-api-key")
ctx := context.Background()
user, err := client.UpsertUser(ctx, &lunogram.UpsertUserRequest{
Identifier: []lunogram.ExternalID{
{ExternalID: "user_123"},
},
Email: lunogram.String("jane@example.com"),
Data: map[string]any{
"first_name": "Jane",
"plan": "pro",
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Upserted user:", user.ID)
}// Custom base URL (e.g. for self-hosted or staging environments)
client := lunogram.NewClient(apiKey, lunogram.WithBaseURL("https://custom.lunogram.io"))
// Custom HTTP client
client := lunogram.NewClient(apiKey, lunogram.WithHTTPClient(&http.Client{
Timeout: 10 * time.Second,
Transport: myTransport,
}))
// Custom timeout (applies to the default HTTP client)
client := lunogram.NewClient(apiKey, lunogram.WithTimeout(10*time.Second))Create a new user or update an existing one. Users are matched by their external identifiers.
user, err := client.UpsertUser(ctx, &lunogram.UpsertUserRequest{
Identifier: []lunogram.ExternalID{
{ExternalID: "user_123"},
},
Email: lunogram.String("jane@example.com"),
Phone: lunogram.String("+31612345678"),
Timezone: lunogram.String("Europe/Amsterdam"),
Locale: lunogram.String("nl-NL"),
Data: map[string]any{
"first_name": "Jane",
"plan": "enterprise",
},
})err := client.DeleteUser(ctx, &lunogram.DeleteUserRequest{
Identifier: []lunogram.ExternalID{
{ExternalID: "user_123"},
},
})Send events that can trigger journeys or update virtual lists. Events are processed asynchronously.
err := client.PostUserEvents(ctx, []lunogram.Event{
{
Name: "purchase_completed",
Identifier: []lunogram.ExternalID{
{ExternalID: "user_123"},
},
Data: map[string]any{
"product_id": "prod_789",
"amount": 99.99,
},
},
})You can also target events at users matching specific data attributes instead of an identifier:
err := client.PostUserEvents(ctx, []lunogram.Event{
{
Name: "feature_announcement",
Match: map[string]any{"plan": "enterprise"},
Data: map[string]any{"feature": "advanced_analytics"},
},
})org, err := client.UpsertOrganization(ctx, &lunogram.UpsertOrganizationRequest{
Identifier: []lunogram.ExternalID{
{ExternalID: "org_456"},
},
Name: lunogram.String("Acme Corp"),
Data: map[string]any{
"industry": "technology",
"size": "enterprise",
},
})Deleting an organization also removes all its user memberships.
err := client.DeleteOrganization(ctx, &lunogram.DeleteOrganizationRequest{
Identifier: []lunogram.ExternalID{
{ExternalID: "org_456"},
},
})err := client.AddOrganizationUser(ctx, &lunogram.OrganizationUserRequest{
Organization: lunogram.OrganizationRef{
Identifier: []lunogram.ExternalID{{ExternalID: "org_456"}},
},
User: lunogram.UserRef{
Identifier: []lunogram.ExternalID{{ExternalID: "user_123"}},
},
Data: map[string]any{
"role": "admin",
"department": "engineering",
},
})err := client.RemoveOrganizationUser(ctx, &lunogram.RemoveOrganizationUserRequest{
Organization: lunogram.OrganizationRef{
Identifier: []lunogram.ExternalID{{ExternalID: "org_456"}},
},
User: lunogram.UserRef{
Identifier: []lunogram.ExternalID{{ExternalID: "user_123"}},
},
})Events are processed asynchronously and can trigger journeys for all users in the organization.
err := client.PostOrganizationEvents(ctx, []lunogram.OrganizationEvent{
{
Name: "subscription_upgraded",
Identifier: []lunogram.ExternalID{
{ExternalID: "org_456"},
},
Data: map[string]any{
"plan": "enterprise",
"seats": 100,
},
},
})Scheduled resources trigger journey entrance recalculation at a specific time or on a recurring interval.
// Single schedule
accepted, err := client.UpsertUserScheduled(ctx, &lunogram.UpsertUserScheduledRequest{
Name: "trial_end",
Identifier: []lunogram.ExternalID{
{ExternalID: "user_123"},
},
ScheduledAt: &trialEnd, // time.Time
Data: map[string]any{
"plan": "pro",
"amount": 29.99,
},
})
// Recurring schedule
accepted, err := client.UpsertUserScheduled(ctx, &lunogram.UpsertUserScheduledRequest{
Name: "weekly_digest",
Identifier: []lunogram.ExternalID{
{ExternalID: "user_123"},
},
Interval: lunogram.String("168h"), // weekly
})
// Delete scheduled resource
err := client.DeleteUserScheduled(ctx, &lunogram.DeleteUserScheduledRequest{
Name: "trial_end",
Identifier: []lunogram.ExternalID{
{ExternalID: "user_123"},
},
})accepted, err := client.UpsertOrganizationScheduled(ctx, &lunogram.UpsertOrganizationScheduledRequest{
Name: "contract_renewal",
Identifier: []lunogram.ExternalID{
{ExternalID: "org_456"},
},
ScheduledAt: &renewalDate,
Data: map[string]any{
"contract_type": "enterprise",
"seats": 100,
},
})
err := client.DeleteOrganizationScheduled(ctx, &lunogram.DeleteOrganizationScheduledRequest{
Name: "contract_renewal",
Identifier: []lunogram.ExternalID{
{ExternalID: "org_456"},
},
})All methods return an *lunogram.APIError when the API responds with a non-2xx status code. You can inspect it for details:
user, err := client.UpsertUser(ctx, req)
if err != nil {
var apiErr *lunogram.APIError
if errors.As(err, &apiErr) {
fmt.Printf("API error %d: %s — %s\n", apiErr.StatusCode, apiErr.Title, apiErr.Detail)
}
return err
}Many requests accept one or more external identifiers. The Source field defaults to "default" when omitted.
// Simple identifier (source defaults to "default")
lunogram.ExternalID{ExternalID: "user_123"}
// Explicit source
lunogram.ExternalID{Source: "stripe", ExternalID: "cus_abc123"}
// With metadata
lunogram.ExternalID{
Source: "hubspot",
ExternalID: "contact_789",
Metadata: map[string]any{"synced": true},
}See LICENSE for details.