lunogram

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 30, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

README

Lunogram Go SDK

Go client library for the Lunogram Client API. Manage user profiles, organizations, events, and scheduled resources from any Go application.

Installation

go get github.com/lunogram/go-sdk

Requires Go 1.22 or later.

Quick start

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)
}

Client options

// 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))

Users

Upsert a user

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",
	},
})
Delete a user
err := client.DeleteUser(ctx, &lunogram.DeleteUserRequest{
	Identifier: []lunogram.ExternalID{
		{ExternalID: "user_123"},
	},
})
Post user events

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"},
	},
})

Organizations

Upsert an organization
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",
	},
})
Delete an organization

Deleting an organization also removes all its user memberships.

err := client.DeleteOrganization(ctx, &lunogram.DeleteOrganizationRequest{
	Identifier: []lunogram.ExternalID{
		{ExternalID: "org_456"},
	},
})
Add a user to an organization
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",
	},
})
Remove a user from an organization
err := client.RemoveOrganizationUser(ctx, &lunogram.RemoveOrganizationUserRequest{
	Organization: lunogram.OrganizationRef{
		Identifier: []lunogram.ExternalID{{ExternalID: "org_456"}},
	},
	User: lunogram.UserRef{
		Identifier: []lunogram.ExternalID{{ExternalID: "user_123"}},
	},
})
Post organization events

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

Scheduled resources trigger journey entrance recalculation at a specific time or on a recurring interval.

User scheduled
// 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"},
	},
})
Organization scheduled
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"},
	},
})

Error handling

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
}

External identifiers

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},
}

License

See LICENSE for details.

Documentation

Overview

Package lunogram provides a Go client for the Lunogram Client API.

Create a client with your API key and use it to manage users, organizations, events, and scheduled resources in your Lunogram project.

client := lunogram.NewClient("your-api-key")
user, err := client.UpsertUser(ctx, &lunogram.UpsertUserRequest{...})

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func String

func String(s string) *string

String returns a pointer to s. Use this to set optional string fields on request types.

client.UpsertUser(ctx, &lunogram.UpsertUserRequest{
    Identifier: []lunogram.ExternalID{{ExternalID: "user_1"}},
    Email:      lunogram.String("user@example.com"),
})

Types

type APIError

type APIError struct {
	StatusCode int
	Title      string `json:"title"`
	Detail     string `json:"detail"`
}

APIError is returned when the API responds with a non-success status code.

func (*APIError) Error

func (e *APIError) Error() string

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client communicates with the Lunogram Client API.

func NewClient

func NewClient(apiKey string, opts ...Option) *Client

NewClient creates a Lunogram client authenticated with the given API key.

func (*Client) AddOrganizationUser

func (c *Client) AddOrganizationUser(ctx context.Context, req *OrganizationUserRequest) error

AddOrganizationUser adds a user to an organization with optional organization-specific data (e.g. role, department).

func (*Client) DeleteOrganization

func (c *Client) DeleteOrganization(ctx context.Context, req *DeleteOrganizationRequest) error

DeleteOrganization deletes an organization and removes all its memberships.

func (*Client) DeleteOrganizationScheduled

func (c *Client) DeleteOrganizationScheduled(ctx context.Context, req *DeleteOrganizationScheduledRequest) error

DeleteOrganizationScheduled deletes all scheduled instances for an organization with the given resource name. This cancels any pending journey entrance states.

func (*Client) DeleteUser

func (c *Client) DeleteUser(ctx context.Context, req *DeleteUserRequest) error

DeleteUser deletes a user by their external identifier(s).

func (*Client) DeleteUserScheduled

func (c *Client) DeleteUserScheduled(ctx context.Context, req *DeleteUserScheduledRequest) error

DeleteUserScheduled deletes all scheduled instances for a user with the given resource name. This cancels any pending journey entrance states.

func (*Client) PostOrganizationEvents

func (c *Client) PostOrganizationEvents(ctx context.Context, events []OrganizationEvent) error

PostOrganizationEvents sends one or more events for organizations. Events are processed asynchronously and can trigger journeys for all users in the targeted organization(s).

func (*Client) PostUserEvents

func (c *Client) PostUserEvents(ctx context.Context, events []Event) error

PostUserEvents sends one or more events for users. Events are processed asynchronously and can trigger journeys or update virtual lists.

Each event is handled independently: if one fails, others may still succeed.

func (*Client) RemoveOrganizationUser

func (c *Client) RemoveOrganizationUser(ctx context.Context, req *RemoveOrganizationUserRequest) error

RemoveOrganizationUser removes a user from an organization.

func (*Client) UpsertOrganization

func (c *Client) UpsertOrganization(ctx context.Context, req *UpsertOrganizationRequest) (*Organization, error)

UpsertOrganization creates or updates an organization by external identifier(s).

func (*Client) UpsertOrganizationScheduled

func (c *Client) UpsertOrganizationScheduled(ctx context.Context, req *UpsertOrganizationScheduledRequest) (*ScheduledAccepted, error)

UpsertOrganizationScheduled creates or updates a scheduled resource for an organization. This triggers journey entrance recalculation for any journeys that depend on the scheduled resource.

func (*Client) UpsertUser

func (c *Client) UpsertUser(ctx context.Context, req *UpsertUserRequest) (*User, error)

UpsertUser creates or updates a user profile. The user is identified by one or more external identifiers. If a user with any of the given identifiers already exists, it is updated; otherwise a new user is created.

func (*Client) UpsertUserScheduled

func (c *Client) UpsertUserScheduled(ctx context.Context, req *UpsertUserScheduledRequest) (*ScheduledAccepted, error)

UpsertUserScheduled creates or updates a scheduled resource for a user. This triggers journey entrance recalculation for any journeys that depend on the scheduled resource.

type DeleteOrganizationRequest

type DeleteOrganizationRequest struct {
	Identifier []ExternalID `json:"identifier"`
}

DeleteOrganizationRequest deletes an organization by identifier.

type DeleteOrganizationScheduledRequest

type DeleteOrganizationScheduledRequest struct {
	// Name of the scheduled resource to delete.
	Name string `json:"name"`

	// Identifier targets a specific organization.
	Identifier []ExternalID `json:"identifier"`
}

DeleteOrganizationScheduledRequest deletes all scheduled instances for an organization with the given name.

type DeleteUserRequest

type DeleteUserRequest struct {
	Identifier []ExternalID `json:"identifier"`
}

DeleteUserRequest deletes a user by identifier.

type DeleteUserScheduledRequest

type DeleteUserScheduledRequest struct {
	// Name of the scheduled resource to delete.
	Name string `json:"name"`

	// Identifier targets a specific user.
	Identifier []ExternalID `json:"identifier,omitempty"`
}

DeleteUserScheduledRequest deletes all scheduled instances for a user with the given name.

type Event

type Event struct {
	// Name of the event (e.g. "purchase_completed").
	Name string `json:"name"`

	// Identifier targets a specific user. Mutually exclusive with Match.
	Identifier []ExternalID `json:"identifier,omitempty"`

	// Match is a JSONB containment filter to target users by data attributes.
	// Mutually exclusive with Identifier.
	Match map[string]any `json:"match,omitempty"`

	// Data holds event-specific attributes.
	Data map[string]any `json:"data"`
}

Event represents a user event to be ingested.

type ExternalID

type ExternalID struct {
	// Source of the identifier (e.g. "default", "anonymous", or a custom source).
	// Defaults to "default" if empty.
	Source string `json:"source,omitempty"`

	// ExternalID is the identifier value in the external system.
	ExternalID string `json:"external_id"`

	// Metadata is optional key-value data associated with this identifier.
	Metadata map[string]any `json:"metadata,omitempty"`
}

ExternalID identifies a user or organization by an external system identifier.

type ExternalIDResponse

type ExternalIDResponse struct {
	ID         string         `json:"id"`
	Source     string         `json:"source"`
	ExternalID string         `json:"external_id"`
	Metadata   map[string]any `json:"metadata,omitempty"`
	CreatedAt  time.Time      `json:"created_at"`
	UpdatedAt  time.Time      `json:"updated_at"`
}

ExternalIDResponse is an external identifier as returned in API responses.

type Option

type Option func(*Client)

Option configures the Client.

func WithBaseURL

func WithBaseURL(url string) Option

WithBaseURL overrides the default API base URL.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient sets a custom http.Client for requests.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the HTTP client timeout. Ignored if WithHTTPClient is also used.

type Organization

type Organization struct {
	ID         string               `json:"id"`
	ProjectID  string               `json:"project_id"`
	Identifier []ExternalIDResponse `json:"identifier"`
	Name       string               `json:"name,omitempty"`
	Data       map[string]any       `json:"data"`
	Version    int32                `json:"version"`
	CreatedAt  time.Time            `json:"created_at"`
	UpdatedAt  time.Time            `json:"updated_at"`
}

Organization represents an organization in Lunogram.

type OrganizationEvent

type OrganizationEvent struct {
	// Name of the event (e.g. "subscription_upgraded").
	Name string `json:"name"`

	// Identifier targets a specific organization. Mutually exclusive with Match.
	Identifier []ExternalID `json:"identifier,omitempty"`

	// Match is a JSONB containment filter to target organizations by data attributes.
	// Mutually exclusive with Identifier.
	Match map[string]any `json:"match,omitempty"`

	// Data holds event-specific attributes.
	Data map[string]any `json:"data,omitempty"`
}

OrganizationEvent represents an organization event to be ingested.

type OrganizationRef

type OrganizationRef struct {
	Identifier []ExternalID `json:"identifier"`
}

OrganizationRef identifies an organization by its external identifiers.

type OrganizationUserRequest

type OrganizationUserRequest struct {
	Organization OrganizationRef `json:"organization"`
	User         UserRef         `json:"user"`

	// Data holds organization-specific attributes for this user (e.g. role, department).
	Data map[string]any `json:"data,omitempty"`
}

OrganizationUserRequest adds a user to an organization.

type RemoveOrganizationUserRequest

type RemoveOrganizationUserRequest struct {
	Organization OrganizationRef `json:"organization"`
	User         UserRef         `json:"user"`
}

RemoveOrganizationUserRequest removes a user from an organization.

type ScheduledAccepted

type ScheduledAccepted struct {
	ID          string         `json:"id"`
	Name        string         `json:"name"`
	ScheduledAt time.Time      `json:"scheduled_at"`
	Data        map[string]any `json:"data,omitempty"`
}

ScheduledAccepted is returned when a scheduled resource is accepted for processing.

type UpsertOrganizationRequest

type UpsertOrganizationRequest struct {
	Identifier []ExternalID   `json:"identifier"`
	Name       *string        `json:"name,omitempty"`
	Data       map[string]any `json:"data,omitempty"`
}

UpsertOrganizationRequest creates or updates an organization.

type UpsertOrganizationScheduledRequest

type UpsertOrganizationScheduledRequest struct {
	// Name of the scheduled resource (e.g. "contract_renewal").
	Name string `json:"name"`

	// Identifier targets a specific organization.
	Identifier []ExternalID `json:"identifier"`

	// ScheduledAt is the trigger time for single schedules.
	ScheduledAt *time.Time `json:"scheduled_at,omitempty"`

	// StartAt is the start time for recurring schedules. Defaults to now if omitted.
	StartAt *time.Time `json:"start_at,omitempty"`

	// Interval for recurring schedules (e.g. "24h"). When set, the schedule becomes recurring.
	Interval *string `json:"interval,omitempty"`

	// Data holds scheduled resource attributes.
	Data map[string]any `json:"data,omitempty"`
}

UpsertOrganizationScheduledRequest creates or updates a scheduled resource for an organization.

type UpsertUserRequest

type UpsertUserRequest struct {
	// Identifier contains one or more external identifiers for the user.
	Identifier []ExternalID `json:"identifier"`

	// Email address of the user.
	Email *string `json:"email,omitempty"`

	// Phone number in E.164 format (e.g. "+31612345678").
	Phone *string `json:"phone,omitempty"`

	// Timezone in IANA format (e.g. "Europe/Amsterdam").
	Timezone *string `json:"timezone,omitempty"`

	// Locale as a BCP 47 tag (e.g. "nl-NL").
	Locale *string `json:"locale,omitempty"`

	// Data holds arbitrary user attributes.
	Data map[string]any `json:"data,omitempty"`
}

UpsertUserRequest creates or updates a user profile.

type UpsertUserScheduledRequest

type UpsertUserScheduledRequest struct {
	// Name of the scheduled resource (e.g. "renewal_date").
	Name string `json:"name"`

	// Identifier targets a specific user.
	Identifier []ExternalID `json:"identifier,omitempty"`

	// ScheduledAt is the trigger time for single schedules.
	ScheduledAt *time.Time `json:"scheduled_at,omitempty"`

	// StartAt is the start time for recurring schedules. Defaults to now if omitted.
	StartAt *time.Time `json:"start_at,omitempty"`

	// Interval for recurring schedules (e.g. "24h"). When set, the schedule becomes recurring.
	Interval *string `json:"interval,omitempty"`

	// Data holds scheduled resource attributes.
	Data map[string]any `json:"data,omitempty"`
}

UpsertUserScheduledRequest creates or updates a scheduled resource for a user.

type User

type User struct {
	ID            string               `json:"id"`
	ProjectID     string               `json:"project_id"`
	Identifier    []ExternalIDResponse `json:"identifier"`
	Email         string               `json:"email,omitempty"`
	Phone         string               `json:"phone,omitempty"`
	Data          map[string]any       `json:"data"`
	Timezone      string               `json:"timezone,omitempty"`
	Locale        string               `json:"locale,omitempty"`
	HasPushDevice bool                 `json:"has_push_device"`
	Version       int32                `json:"version"`
	CreatedAt     time.Time            `json:"created_at"`
	UpdatedAt     time.Time            `json:"updated_at"`
}

User represents a user profile in Lunogram.

type UserRef

type UserRef struct {
	Identifier []ExternalID `json:"identifier"`
}

UserRef identifies a user by its external identifiers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL