@eggai-sdk/core
eggai — the EGGai Node.js SDK
Four SDKs, one behaviour model. This package is the official Node.js client for api.eggai.tech: offer optimization, lead generation, survey response and assortment optimization over EGGai-v1, a Large Behavior Model grounded in real till history.
Zero runtime dependencies — written in TypeScript, compiled to ESM on the
global fetch, declarations included. Node 18+.
npm install @eggai-sdk/core
Requirements
| Floor | Why | |
|---|---|---|
| Node | 18.0+ | global fetch (18.0) and AbortSignal.timeout (17.3) — no HTTP dependency, no polyfill |
| TypeScript | 4.7+ | declarations resolve through the exports map, so moduleResolution must be node16, nodenext or bundler |
This package is ESM-only — "type": "module", no CommonJS build:
import { EggAI } from '@eggai-sdk/core'; // any supported Node
const { EggAI } = await import('@eggai-sdk/core'); // from CommonJS
require('@eggai-sdk/core') resolves only on Node 22.12+ / 20.19+, where
require() of ESM landed; on earlier 18.x and 20.x it throws
ERR_REQUIRE_ESM — use the dynamic import() above.
Authenticate
Every client takes a workspace API key (issued in the EGGai app under
Settings → API keys). Pass it directly, or set EGGAI_API_KEY once:
export EGGAI_API_KEY="pk_live_..."
Practice keys work everywhere live keys do, but are capped per job so you
can integrate without spending real volume. EGGAI_BASE_URL overrides the
endpoint when you are pointed at staging.
The four products
import { EggAI } from '@eggai-sdk/core';
const client = new EggAI(); // reads EGGAI_API_KEY
// 1 · Offer Optimization — what is the least this customer would accept?
const terms = await client.offers.cheapestAccepted({
customer: 'c_8813',
profile: 'shops fortnightly, small baskets, coupon-led',
offer: 'Fresh Picks Weekend — 15% off produce, min spend 500',
guidelines: 'discount caps at 12%, min spend above 300.',
});
terms.text; // "8% off produce, min spend 349"
terms.tagline; // "Weekend Picks"
terms.changed; // ["discount", "min_spend", "tagline"]
terms.objection; // "threshold above my usual basket"
// 2 · Lead Generation — which of these people is worth the call?
const ranked = await client.leads.rank(
{ c_8813: 'fortnightly, coupon-led', c_2291: 'weekly family shop' },
{
offer: 'Fresh Picks Weekend — 15% off produce',
guidelines: 'hold the 15%, the aisle may change',
tailorOffer: true,
},
);
ranked.at(0).score; // 0.71
ranked.at(0).reason; // "buys dairy most weeks, produce rarely"
ranked.above(0.5); // the slice worth calling
// 3 · Survey Response (preview) — what would a panel of your customers say?
const result = await client.surveys.ask(
'Would you switch to our own-brand line?',
{
options: ['Definitely', 'Maybe', 'No'],
panel: 'shoppers aged 18-55 who buy fresh produce most weeks',
n: 150,
},
);
result.distribution; // { Definitely: 0.21, Maybe: 0.44, No: 0.35 }
result.crosstab('segment'); // the same spread, split by trait
// 4 · Assortment Optimization — who follows the shelf, and who walks?
const impact = await client.assortment.impact(customers, {
scenario: {
kind: 'delist',
category: 'chilled juice',
change: 'Tropicana Orange 1L 119',
remaining: 'own-label 1L 79 · Malee 1L 95',
},
});
impact.retained; // 0.91 of category spend kept
impact.counts; // { switches: 214, leaves_store: 6 }
impact.watchlist; // who walks, and the basket at stake
impact.rows[0].substitute; // where the demand lands
Or import a product directly, as the product pages show:
import { OfferOptimizer } from '@eggai-sdk/core';
const terms = await new OfferOptimizer(apiKey).cheapestAccepted({ ... });
Jobs, streaming, results
Every batch call is a job on EGGai's servers. wait: false hands you the
job instead of the result:
const job = await client.leads.rank(customers, { offer, wait: false });
for await (const ev of job.stream()) { // progress / live / twin events
console.log(ev.stage ?? ev.text);
}
const ranked = new RankedLeads(await job.resultRows());
client.assortment.preview(panel, { scenario }) answers sampled what-ifs
synchronously (≤64 rows, nothing persisted). client.score(rows) returns
bare probabilities for up to 64 rows. client.usage('2026-08') is the
same metering rollup billing reads.
Errors
Everything thrown on purpose subclasses EggAIError and carries the
server's own message: AuthenticationError, InvalidRequestError,
RateLimitError, FeatureNotEnabledError (Survey Response is preview,
enabled per workspace), JobFailedError (with .job), APIError.
Docs
Full documentation: docs.eggai.tech — concepts, CSV schemas, the REST reference underneath this client, and the studies behind each product.