SDKs

TypeScript SDK

Installation

npm install levelfour

Client Setup

import { LevelFourClient } from "levelfour";

const client = new LevelFourClient({
    apiKey: "l4_live_...",
    baseURL: "https://api.levelfour.ai",
    timeoutInSeconds: 30,
    maxRetries: 2,
});

With environment variable auto-detection:

const client = new LevelFourClient();

Constructor Options

ParameterTypeDefault
apiKeystringLEVELFOUR_API_KEY env var
baseURLstringhttps://api.levelfour.ai
timeoutInSecondsnumber30
maxRetriesnumber2
fetchtypeof fetchBuilt-in
defaultHeadersRecord<string, string>undefined

Recommendations

const summary = await client.recommendations.getSavingsByProvider();

const potential = await client.recommendations.getPotentialSavings();

const overview = await client.recommendations.getOverview();

const processing = await client.recommendations.listInProgress();

const detail = await client.recommendations.get({ recommendation_id: "rec_123" });

const page = await client.recommendations.list({
    page: 1,
    page_size: 50,
    sort_by: "monthly_savings",
    sort_order: "desc",
});

Recommendations Audit

const summary = await client.recommendations.audit.getSummary();

const page = await client.recommendations.audit.list({
    page: 1,
    page_size: 50,
    sort_by: "monthly_savings",
    sort_order: "desc",
    start: "2025-01-01",
    end: "2025-03-31",
    preset: "6M",
    provider: "aws",
    service: ["EC2", "RDS"],
    environment: ["production"],
    account_id: ["123456789012"],
});

Costs

const summary = await client.costs.getSummary();

const breakdown = await client.costs.list({
    format: "table",
    period: "2025-03",
    provider_id: "aws",
    page: 1,
    page_size: 50,
    sort_by: "cost",
    sort_order: "desc",
});

const daily = await client.costs.getDailyCosts({
    start: "2025-03-01",
    end: "2025-03-31",
});

const monthly = await client.costs.getMonthlyCosts();

Providers

const providers = await client.providers.list();

const top = await client.providers.getTopRecommendations({ provider_id: "aws" });

const recs = await client.providers.listRecommendations({
    provider_id: "aws",
    page: 1,
    page_size: 50,
    sort_by: "monthly_savings",
    sort_order: "desc",
    service: ["EC2"],
    display_status: ["available", "pending"],
});

const overview = await client.providers.getRecommendationsOverview({ provider_id: "aws" });

const savings = await client.providers.listRealizedSavings({
    provider_id: "aws",
    page: 1,
    page_size: 50,
    preset: "6M",
});

const savingsSummary = await client.providers.getRealizedSavingsSummary({ provider_id: "aws" });

const spending = await client.providers.getCostsSummary({ provider_id: "aws" });

const spendingList = await client.providers.listCosts({
    provider_id: "aws",
    format: "table",
    page: 1,
    page_size: 50,
});

API Keys

const keys = await client.apiKeys.list();

const newKey = await client.apiKeys.create({
    name: "CI Pipeline",
    scope: "read",
});

await client.apiKeys.revoke({ key_id: "key_123" });

const rotated = await client.apiKeys.rotate({ key_id: "key_123" });

Webhooks

const endpoints = await client.webhooks.list();

const endpoint = await client.webhooks.register({
    url: "https://example.com/webhook",
    event_types: ["recommendation.accepted", "optimization.completed"],
});

await client.webhooks.delete({ endpoint_id: "ep_123" });

Auth

const me = await client.auth.getWhoami();

Pagination

Methods that return paginated results return a Page object that implements AsyncIterable. You can auto-iterate items, manually navigate pages, or collect everything into an array.

Auto-iterate all items

for await (const rec of await client.recommendations.list({ page_size: 50 })) {
    console.log(rec.recommendation_id);
}

Manual pagination

const page = await client.recommendations.list({ page: 1, page_size: 50 });
console.log(page.data);

while (page.hasNextPage()) {
    await page.getNextPage();
    console.log(page.data);
}

Collect all items

import { collectAll } from "levelfour";

const allRecs = await collectAll(await client.recommendations.list());

See Pagination for more details.

Error Handling

import {
    LevelFourClient,
    NotFoundError,
    RateLimitError,
    LevelFourError,
} from "levelfour";

const client = new LevelFourClient();

try {
    await client.recommendations.get({ recommendation_id: "rec_nonexistent" });
} catch (err) {
    if (err instanceof NotFoundError) {
        console.log("Not found:", err.message);
    } else if (err instanceof RateLimitError) {
        console.log("Rate limited");
    } else if (err instanceof LevelFourError) {
        console.log(`API error ${err.statusCode}: ${err.message}`);
    }
}

All error classes extend LevelFourError which exposes statusCode, body, and rawResponse properties.

See Error Handling for the full error class hierarchy.

Webhook Verification

import { WebhookVerifier, WebhookVerificationError } from "levelfour";

const verifier = new WebhookVerifier("whsec_your_signing_secret");

try {
    const payload = verifier.verify(requestBody, {
        "webhook-id": headers["webhook-id"],
        "webhook-timestamp": headers["webhook-timestamp"],
        "webhook-signature": headers["webhook-signature"],
    });
    console.log("Verified event:", payload.type);
} catch (err) {
    if (err instanceof WebhookVerificationError) {
        console.log("Verification failed:", err.message);
    }
}

Custom timestamp tolerance (default is 300 seconds):

const payload = verifier.verify(body, headers, { toleranceSeconds: 600 });

See Webhooks for event types and payload details.

Raw Response Access

Every API method returns an HttpResponsePromise that can be awaited directly for parsed data, or unwrapped to access HTTP metadata:

const data = await client.costs.getSummary();

const { data: costs, rawResponse } = await client.costs
    .getSummary()
    .withRawResponse();

console.log(rawResponse.status);
console.log(rawResponse.headers);

Request Options

Override client defaults on a per-request basis by passing a second argument:

const summary = await client.recommendations.getSavingsByProvider({
    timeoutInSeconds: 60,
    maxRetries: 5,
    headers: { "X-Request-Id": "abc123" },
    abortSignal: controller.signal,
});
OptionTypeDescription
timeoutInSecondsnumberOverride request timeout
maxRetriesnumberOverride max retry attempts
headersRecord<string, string>Extra headers for this request
abortSignalAbortSignalCancel the request