TypeScript SDK
Use the TypeScript SDK in JavaScript or TypeScript for evaluation, models, forecasting and outcomes.
npm install spavikimport { Spavik } from 'spavik';
const spavik = new Spavik(); // reads SPAVIK_API_KEY
const model = await spavik.train('churn.csv', { target: 'churn' });
console.log(await model.predict({ plan: 'pro', seats: 12, tickets_90d: 3 }));No production dependency. Node 22 or later, and edge runtimes that provide
fetch and WebCrypto. ESM and CommonJS.
The operations
| Call | Cost |
|---|---|
spavik.evaluate(data, { target }) | free |
spavik.train(data, { target }) | free |
spavik.backtest(data, { target, horizon }) | free |
spavik.forecast(data, { target, horizon }) | horizon × the engine's credits per call, all series included; 1 credit per step on the default engine |
spavik.models() | free |
spavik.usage() | free |
model.predict(rows) | the engine's credits per row, 1 on the default engine |
model.submitOutcomes(outcomes) | free |
model.performance(days) | free |
model.refresh() | free |
model.archive(), spavik.archive(modelId) | free |
spavik.restore(modelId) | free |
models() is an async iterator; models({ status: 'archived' }) lists what
was set aside. A model exposes its engine and features, the columns predict expects.
The raw API fields are in model.data, for example model.data.target.
for await (const model of spavik.models()) console.log(model.id, model.name, model.features);Engine selection
Training and evaluation accept engineFamily; omitting it lets the API choose its
configured default family. Read GET /v1/engine-families for available families.
The model's family cannot change. Predictions accept engineVersion to select an
exact version or latest alias within that family. Forecast and backtest accept
both options. Available versions and prices come from GET /v1/engines.
Keep the exact engine from the prediction result with its model version.
Evaluate before training
const report = await spavik.evaluate('churn.csv', { target: 'churn' });
console.log(report.assessment.status);
console.log(report.assessment.metric, report.assessment.message);
console.log(report.hint);
const check = await spavik.backtest('sales.csv', {
target: 'sales', horizon: 7, timestamp: 'date', itemId: 'store',
});
console.log(check.assessment);
console.log(check.backtest);Both operations are free and preserve the API's assessment. See
Read an evaluation for comparison statuses, random-holdout
limits and forecasting protocol metadata. A high score does not establish
reliability or prove leakage.
backtest() also accepts pointEstimate (mean or median) and quantiles.
Use the same settings for backtesting and forecasting so the evaluation matches
the forecast you intend to request.
Configure and forecast
const spavik = new Spavik({
apiKey: process.env.SPAVIK_API_KEY,
});
const forecast = await spavik.forecast('sales.csv', {
target: 'sales', horizon: 7, timestamp: 'date', itemId: 'store',
pointEstimate: 'mean',
});
console.log(await forecast.points()); // asynchronous method in JavaScript
console.log(forecast.csv);
console.log(forecast.get('point_estimate'), forecast.predictionsRemaining);forecast.predictionsRemaining copies the X-Spavik-Predictions-Remaining
header, or is null when the API does not send it. forecast.cost is null:
the API does not return the amount charged. See Costs and credits.
forecast() also accepts quantiles and pointEstimate: 'median'. Keep these
settings consistent with the backtest you actually ran. Both methods use
itemId in JavaScript; the REST field is item_id.
The key travels in X-Spavik-Api-Key. If apiKey is omitted, the client reads
SPAVIK_API_KEY; an explicitly empty string disables that fallback.
baseUrl and SPAVIK_BASE_URL are optional overrides. The SDK defaults to
https://api.spavik.co. Set an override only when needed; baseUrl takes precedence.
Outcomes, refresh and archives
const submitted = await model.submitOutcomes(outcomes);
console.log(submitted.get('accepted'), submitted.get('refresh_triggered'));
const performance = await model.performance(30);
const observed = performance?.get('observed');
console.log(observed?.assessment, observed?.metrics, observed?.baselines);
console.log(observed?.metric_context, performance?.get('evaluation'));Batch outcomes. When refresh_triggered is true, automatic integration has
already started; do not request a duplicate refresh. A manual refresh()
returns the integration details, including incomplete_outcomes, or null
only for NO_PENDING_OUTCOMES. Other API failures are raised, including
performance-service failures. observed: null means there are no client-reported
predictions to compare yet. Scores, baselines, undefined reasons and
observed.assessment come directly from the API; the SDK does not recalculate them.
model.archive() frees an active-model slot without deleting artifacts.
spavik.restore(modelId) restores by identifier. Permanent deletion is outside
the SDK's surface. See the feedback loop.
Types
The SDK is written in TypeScript and ships compiled JavaScript for ESM and
CommonJS, with generated type declarations. JavaScript applications need no
TypeScript setup. Autocompletion covers operations, options and error classes.
The npm package is named spavik. verify is exported from spavik, and also
from spavik/webhooks for bundles that want only webhook verification: both
paths give the same function.
The same refusals as Python
What the Python library refuses, this one refuses too, with the same message and at the same moment. A conformance suite replays one scenario across both, and across the MCP server.
await spavik.train(eightRows, { target: 'churn' });
// SpavikDataError: training needs at least 10 rows, 8 provided. Nothing was sent.Errors
import { SpavikCreditsExhausted } from 'spavik';
try {
await model.predict(rows);
} catch (exc) {
if (!(exc instanceof SpavikCreditsExhausted)) throw exc;
console.error(exc.code, exc.detail, exc.requestId);
console.error((await spavik.usage()).predictions_remaining);
}Every class extends SpavikError and carries code, detail, status and
requestId: SpavikDataError, SpavikAuthError, SpavikCreditsExhausted,
SpavikQuotaExceeded, SpavikValidationError, SpavikNotFound,
SpavikConflict, SpavikRateLimited, SpavikUnavailable, SpavikServerError,
SpavikTransportError and SpavikSignatureError. They match the Python
classes, under the same names.
Webhooks
verify checks the Spavik-Signature header and returns the decoded event.
It rejects with SpavikSignatureError when the signature, the timestamp or the
header is invalid. Pass the raw body, never a parsed one. See
Webhooks for the delivery contract.
Express:
import express from 'express';
import { SpavikSignatureError, verify } from 'spavik';
const app = express();
app.post('/webhooks/spavik', express.raw({ type: 'application/json' }), async (req, res) => {
let event;
try {
event = await verify(req.body, req.headers, process.env.SPAVIK_WEBHOOK_SECRET);
} catch (error) {
if (error instanceof SpavikSignatureError) return res.sendStatus(400);
throw error;
}
// Store event.id and the event durably, then acknowledge.
res.sendStatus(204);
});Hono:
import { Hono } from 'hono';
import { SpavikSignatureError, verify } from 'spavik';
const app = new Hono();
app.post('/webhooks/spavik', async (c) => {
let event;
try {
event = await verify(await c.req.arrayBuffer(), c.req.raw.headers, c.env.SPAVIK_WEBHOOK_SECRET);
} catch (error) {
if (error instanceof SpavikSignatureError) return c.body(null, 400);
throw error;
}
// Store event.id and the event durably, then acknowledge.
return c.body(null, 204);
});Retries
The SDK follows the API retry policy for network failures, eligible 429/5xx
responses and REQUEST_IN_PROGRESS. It keeps the same idempotency key during
internal attempts. Completed responses can be replayed within the API retention
window, 24 hours by default from operation creation.
Keep error.idempotencyKey and error.requestId after an uncertain failure.
A new SDK method call generates a new key and can repeat an operation that
already succeeded. Do not retry blindly or switch keys to bypass
REQUEST_INTERRUPTED or IDEMPOTENCY_RESPONSE_UNAVAILABLE.
maxRetries counts every attempt, the first included: the default is 3, and
maxRetries: 1 disables retries. Without Retry-After, the default backoff is
exponential with jitter, from 0.5 s up to 8 s. Each operation has its own
timeout, from 15 s for reads to 120 s for training; timeout, in milliseconds,
applies to calls without one.
new Spavik({ maxRetries: 1 }); // a single attempt, no retry
new Spavik({ backoff: (n) => n * 1000 }); // linear backoff in ms, n is the attempt numberDebugging
SPAVIK_LOG=debug node my-script.jsHow is this guide?
Last updated on