Guide
Batch runs with CSV
One row per customer, contact, respondent or panel member — the same files the EGGai app accepts, run through the SDKs.
The SDKs take rows as plain dicts/objects, so a CSV batch is: read the file, map the rows, make one call. The column names below match the app's uploaders exactly — a file that works there works here.
The schemas
| Product | Required | Optional |
|---|---|---|
| Offer Optimization | customer_id, profile |
offer — free text per row. Omit the column to run one campaign across the whole
file. |
| Lead Generation | customer_id, profile |
Extra columns are kept as traits. The offer is set once for the run, not per row. |
| Survey Response | question |
options — pipe-separated; empty for open-ended. Own respondents:
customer_id, profile. |
| Assortment panel | customer_id, profile |
audience, segment, store_type, category_spend, total_spend, current_sku — spend
columns weight the answer; the rest cut the result. |
Offer batch from a CSV
customer_id,profile,offer c_8813,"fortnightly, coupon-led","15% off produce, min 500" c_2291,"weekly family shop","15% off produce, min 500"
import csv from eggai import EggAI client = EggAI() with open("offers.csv", newline="") as f: rows = list(csv.DictReader(f)) # customer_id is mapped for you run = client.offers.cheapest_accepted( rows=rows, guidelines="discount caps at 12%", on_progress=lambda p: print(f"{p['pct']:.0f}%"), ) with open("terms.csv", "w", newline="") as f: w = csv.writer(f) w.writerow(["customer_id", "accepted", "terms", "tagline"]) for t in run: w.writerow([t.customer, t.accepted, t.text, t.tagline])
import { parse } from 'csv-parse/sync'; // or any CSV reader import { readFileSync, writeFileSync } from 'node:fs'; import { EggAI } from '@eggai-sdk/core'; const client = new EggAI(); const rows = parse(readFileSync('offers.csv'), { columns: true }); const run = await client.offers.cheapestAccepted({ rows, // customer_id is mapped for you guidelines: 'discount caps at 12%', onProgress: (p) => console.log(`${p.pct}%`), }); const out = [['customer_id', 'accepted', 'terms', 'tagline']]; for (const t of run) out.push([t.customer, t.accepted, t.text, t.tagline]); writeFileSync('terms.csv', out.map((r) => r.join(',')).join('\n'));
Column mapping is automatic. Rows may use
customer_id (the
CSV convention) or id (the API's) — the SDKs map one to the other. Every other column
rides along untouched.A lead list, capped to your call capacity
ranked = client.leads.rank(
rows, # customer_id, profile [, traits…]
offer = "Fresh Picks Weekend — 15% off produce",
max_leads = 400, # what six people can call in 2 days
)
for lead in ranked.qualified:
print(lead.rank, lead.id, lead.score, lead.reason)
const ranked = await client.leads.rank(rows, { offer: 'Fresh Picks Weekend — 15% off produce', maxLeads: 400, // what six people can call in 2 days }); for (const lead of ranked.qualified) { console.log(lead.rank, lead.id, lead.score, lead.reason); }
An assortment panel
customer_id,profile,category_spend,total_spend c_8813,"buys the leaving line every visit",610,7900 c_2291,"weekly juice, own-label leaning",480,5400
The two spend columns matter: category_spend weights retention,
total_spend sizes what walks with a store leaver. Without them every customer counts
equally — directionally fine, financially wrong.
Practice first
Run the file with your practice key first. It authenticates identically but caps rows per job, so a malformed file or a wrong guideline costs you nothing. When the output reads right, switch keys and run the list.