SDKs

Go SDK

Installation

go get github.com/LevelFourAI/levelfour-go@v0.1.0

Client Setup

import "github.com/LevelFourAI/levelfour-go/levelfour"

client, err := levelfour.NewClient("l4_live_...")
if err != nil {
    log.Fatal(err)
}

With environment variable auto-detection (pass empty string):

client, err := levelfour.NewClient("")

Client Options

client, err := levelfour.NewClient("l4_live_...",
    levelfour.WithBaseURL("https://api.staging.levelfour.ai"),
    levelfour.WithMaxRetries(3),
    levelfour.WithHTTPClient(&http.Client{Timeout: 60 * time.Second}),
)
OptionFunctionDefault
Base URLlevelfour.WithBaseURL(url)https://api.levelfour.ai
Max Retrieslevelfour.WithMaxRetries(n)2
No Retrieslevelfour.WithNoRetries()Retries enabled
HTTP Clientlevelfour.WithHTTPClient(c)30s timeout

Recommendations

Account-wide methods take only ctx. Per-provider variants live on the same namespace and take a providerID string ("aws", "gcp", "azure", "k8s").

ctx := context.Background()

savings, err := client.Recommendations.GetSavingsByProvider(ctx)
potential, err := client.Recommendations.GetPotentialSavings(ctx)
overview, err := client.Recommendations.GetOverview(ctx)
processing, err := client.Recommendations.ListInProgress(ctx)
detail, err := client.Recommendations.Get(ctx, "rec_123")

page, err := client.Recommendations.List(ctx, &levelfour.ListRecommendationsRequest{
    Page:      levelfour.Int(1),
    PageSize:  levelfour.Int(50),
    SortBy:    levelfour.String("monthly_savings"),
    SortOrder: levelfour.String("desc"),
})

top, err := client.Recommendations.GetTop(ctx, "aws")

recs, err := client.Recommendations.ListByProvider(ctx, "aws",
    &levelfour.ListByProviderRecommendationsRequest{
        Page:          levelfour.Int(1),
        PageSize:      levelfour.Int(50),
        SortBy:        levelfour.String("monthly_savings"),
        SortOrder:     levelfour.String("desc"),
        Service:       []string{"EC2"},
        DisplayStatus: []string{"available", "pending"},
    },
)

providerOverview, err := client.Recommendations.GetProviderOverview(ctx, "aws")
providerFilters, err := client.Recommendations.GetProviderFilters(ctx, "aws")
providerPotential, err := client.Recommendations.GetProviderPotentialSavingsSummary(ctx, "aws")

providerPotentialPage, err := client.Recommendations.ListProviderPotentialSavings(ctx, "aws",
    &levelfour.ListProviderPotentialSavingsRecommendationsRequest{
        Page:     levelfour.Int(1),
        PageSize: levelfour.Int(50),
    },
)

activity, err := client.Recommendations.GetRecommendationActivity(ctx, "rec_123")

_, err = client.Recommendations.AddRejectionFeedback(ctx, "rec_123",
    &levelfour.RejectionFeedbackRequest{
        Reason: levelfour.String("not applicable in this account"),
    },
)

Recommendations Audit

Realized savings (audited completions). Account-wide and per-provider variants both live on client.Recommendations.Audit.

summary, err := client.Recommendations.Audit.GetSummary(ctx)

page, err := client.Recommendations.Audit.List(ctx, &levelfour.ListAuditRequest{
    Page:      levelfour.Int(1),
    PageSize:  levelfour.Int(50),
    SortBy:    levelfour.String("monthly_savings"),
    SortOrder: levelfour.String("desc"),
    Start:     levelfour.String("2025-01-01"),
    End:       levelfour.String("2025-03-31"),
})

providerSummary, err := client.Recommendations.Audit.GetProviderSummary(ctx, "aws")

providerPage, err := client.Recommendations.Audit.ListByProvider(ctx, "aws",
    &levelfour.ListByProviderAuditRequest{
        Page:     levelfour.Int(1),
        PageSize: levelfour.Int(50),
    },
)

Audit

Top-level realized-savings detail for a single audited recommendation.

detail, err := client.Audit.GetRealizedAuditDetail(ctx, "rec_123")

Costs

Account-wide methods take only ctx. Per-provider variants take a providerID.

summary, err := client.Costs.GetSummary(ctx)

breakdown, err := client.Costs.List(ctx, &levelfour.ListCostsRequest{
    Format:    levelfour.String("table"),
    Period:    levelfour.String("2025-03"),
    Page:      levelfour.Int(1),
    PageSize:  levelfour.Int(50),
    SortBy:    levelfour.String("cost"),
    SortOrder: levelfour.String("desc"),
})

daily, err := client.Costs.GetDailyCosts(ctx, &levelfour.GetDailyCostsCostsRequest{
    Start: levelfour.String("2025-03-01T00:00:00.000Z"),
    End:   levelfour.String("2025-03-31T00:00:00.000Z"),
})

monthly, err := client.Costs.GetMonthlyCosts(ctx)

providerSummary, err := client.Costs.GetProviderSummary(ctx, "aws")

providerFilters, err := client.Costs.GetProviderFilters(ctx, "aws",
    &levelfour.GetProviderFiltersCostsRequest{},
)

providerList, err := client.Costs.ListByProvider(ctx, "aws",
    &levelfour.ListByProviderCostsRequest{
        Format:   levelfour.String("table"),
        Page:     levelfour.Int(1),
        PageSize: levelfour.Int(50),
    },
)

timeline, err := client.Costs.GetProviderTimeline(ctx, "aws",
    &levelfour.GetProviderTimelineCostsRequest{
        Start: levelfour.String("2025-01-01T00:00:00.000Z"),
        End:   levelfour.String("2025-03-31T00:00:00.000Z"),
    },
)

Providers

providers, err := client.Providers.List(ctx)

client.Providers only lists connected providers. Per-provider drill-downs live on the resource-owning namespaces: cost data on client.Costs.GetProviderSummary / client.Costs.ListByProvider / client.Costs.GetProviderTimeline, recommendation data on client.Recommendations.ListByProvider / client.Recommendations.GetProviderOverview, and audit data on client.Recommendations.Audit.ListByProvider / client.Recommendations.Audit.GetProviderSummary.

Accounts

Manage connected cloud accounts and integration installations.

accounts, err := client.Accounts.ListConnectedAccounts(ctx,
    &levelfour.ListConnectedAccountsAPIV1AccountsGetRequest{},
)

integration, err := client.Accounts.CreateIntegration(ctx, &levelfour.CreateIntegrationRequest{
    Provider:  "aws",
    AccountID: "123456789012",
    Name:      "Production",
})

status, err := client.Accounts.GetIntegrationStatus(ctx, "integration_123")

modules, err := client.Accounts.ListCustomerModules(ctx)

installs, err := client.Accounts.ListGithubInstallations(ctx)

_, err = client.Accounts.CompleteGithubIntegration(ctx, &levelfour.GithubCompleteRequest{
    InstallationID: 12345,
})

Health

API readiness probes. Useful as a CI smoke test before larger calls.

ready, err := client.Health.HealthReady(ctx)

// Lightweight variant: issues a HEAD request and returns no body.
err = client.Health.HealthReadyHead(ctx)

API Keys

keys, err := client.APIKeys.List(ctx)

newKey, err := client.APIKeys.Create(ctx, &levelfour.CreateAPIKeyRequest{
    Name: "CI Pipeline",
})

_, err = client.APIKeys.Revoke(ctx, "key_123")

rotated, err := client.APIKeys.Rotate(ctx, "key_123")

Webhooks

endpoints, err := client.Webhooks.List(ctx)

endpoint, err := client.Webhooks.Register(ctx, &levelfour.RegisterEndpointRequest{
    URL:        "https://example.com/webhook",
    EventTypes: []string{"recommendation.accepted", "optimization.completed"},
})

_, err = client.Webhooks.Delete(ctx, "ep_123")

Auth

me, err := client.Auth.GetWhoami(ctx)

Pagination

Paginated methods return a core.Page with typed items. You can iterate with the built-in iterator, collect all results, or navigate pages manually.

Auto-iterate with Iterator

page, err := client.Recommendations.List(ctx, &levelfour.ListRecommendationsRequest{
    PageSize: levelfour.Int(50),
})
if err != nil {
    log.Fatal(err)
}

iter := page.Iterator()
for iter.Next(ctx) {
    rec := iter.Current()
    fmt.Printf("%s: $%.2f/mo\n", rec.Service, rec.MonthlySavings)
}
if err := iter.Err(); err != nil {
    log.Fatal(err)
}

Collect all items

page, err := client.Recommendations.List(ctx, &levelfour.ListRecommendationsRequest{
    PageSize: levelfour.Int(50),
})
if err != nil {
    log.Fatal(err)
}

allRecs, err := levelfour.CollectAll(ctx, page)
if err != nil {
    log.Fatal(err)
}

Manual page navigation

page, err := client.Recommendations.List(ctx, &levelfour.ListRecommendationsRequest{
    PageSize: levelfour.Int(50),
})
if err != nil {
    log.Fatal(err)
}

for {
    for _, rec := range page.Results {
        fmt.Println(rec.RecommendationID)
    }
    nextPage, err := page.GetNextPage(ctx)
    if errors.Is(err, core.ErrNoPages) {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    page = nextPage
}

See Pagination for more details.

Error Handling

Go errors are typed structs that can be inspected with errors.As:

import "errors"

detail, err := client.Recommendations.Get(ctx, "rec_nonexistent")
if err != nil {
    var notFoundErr *levelfour.NotFoundError
    var rateLimitErr *levelfour.TooManyRequestsError
    var badReqErr *levelfour.BadRequestError

    switch {
    case errors.As(err, &notFoundErr):
        fmt.Printf("Not found: %v\n", notFoundErr.Body)
    case errors.As(err, &rateLimitErr):
        fmt.Printf("Rate limited: %v\n", rateLimitErr.Body)
    case errors.As(err, &badReqErr):
        fmt.Printf("Bad request: %v\n", badReqErr.Body)
    default:
        fmt.Printf("Error: %v\n", err)
    }
}

See Error Handling for the full error type hierarchy.

Webhook Verification

import "github.com/LevelFourAI/levelfour-go/levelfour/webhooks"

verifier, err := webhooks.NewVerifier("whsec_your_signing_secret")
if err != nil {
    log.Fatal(err)
}

payload, err := verifier.Verify(r.Header, body)
if err != nil {
    http.Error(w, "Invalid signature", http.StatusUnauthorized)
    return
}

fmt.Printf("Verified event: %v\n", payload["type"])

Custom timestamp tolerance (default is 5 minutes):

payload, err := verifier.VerifyWithTolerance(r.Header, body, 10*time.Minute)

Full HTTP Handler Example

package main

import (
    "fmt"
    "io"
    "log"
    "net/http"

    "github.com/LevelFourAI/levelfour-go/levelfour/webhooks"
)

func main() {
    verifier, err := webhooks.NewVerifier("whsec_your_signing_secret")
    if err != nil {
        log.Fatal(err)
    }

    http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
        body, err := io.ReadAll(r.Body)
        if err != nil {
            http.Error(w, "failed to read body", http.StatusBadRequest)
            return
        }

        payload, err := verifier.Verify(r.Header, body)
        if err != nil {
            http.Error(w, "invalid signature", http.StatusUnauthorized)
            return
        }

        fmt.Printf("Received event: %v\n", payload)
        w.WriteHeader(http.StatusOK)
    })

    log.Println("Listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

See Webhooks for event types and payload details.

Request Options

Override client defaults on a per-request basis using the option package:

import "github.com/LevelFourAI/levelfour-go/option"

summary, err := client.Recommendations.GetSavingsByProvider(ctx,
    option.WithMaxAttempts(5),
    option.WithHTTPHeader(http.Header{
        "X-Request-Id": []string{"abc123"},
    }),
)
OptionFunctionDescription
Base URLoption.WithBaseURL(url)Override base URL
HTTP Clientoption.WithHTTPClient(c)Custom HTTP client
Headersoption.WithHTTPHeader(h)Extra headers
Max Attemptsoption.WithMaxAttempts(n)Override retry attempts
Auth Tokenoption.WithToken(t)Override Bearer token
Query Paramsoption.WithQueryParameters(v)Extra query parameters
Body Propertiesoption.WithBodyProperties(m)Extra body properties