API Documentation
Integrate the Will I Sell IT? valuation, buyer-archetype, confidence-scoring, and platform-fit engine into your own application. Drop-in JavaScript library. No server, no API keys, no rate limits — runs entirely client-side. Apache 2.0 licensed for non-commercial use; contact us for commercial integrations.
version: v0.6 · status: beta · size: ~280 KB minified · ~85 KB gzipped
1. Overview
The synthesis engine is a pure-JavaScript library exposed as three global functions on window:
| Symbol | Type | Purpose |
|---|---|---|
window.__willisellitSynth(answers, isPrerev, opts?) | function | Main entry point. Returns a complete synthesis object. |
window.__willisellitSelfTest(opts?) | function | Runs 15 self-test fixtures. Returns a summary object. |
window.__willisellitRender | object | HTML render helpers: .archetype(), .flags(), .fixCard(). |
The library is dependency-free, deterministic (same inputs → same outputs), and side-effect-free apart from optional console.warn on developer mistakes (unknown archetype id, malformed opts, etc.).
synth, synth.story, and synth.buyer is considered stable between minor versions. Internal helpers (platformFit, comboFlags, etc.) are not part of the public API and may change without notice. Use the documented top-level functions only.
2. Quickstart
2.1 Drop-in script tag
Embed the willisellit script directly in your page. The synthesis engine is part of the main bundle at /index.html; you can either fetch and embed the script section or self-host the engine module.
// Minimum integration — embed the bundle and call caseSynthesis const answers = { asset: 'saas', industry: 'SaaS / Productivity', audience: 'smb', stage: 'est', revenueType: 'recurring', mrr: '15000', margin: 'm50_70', trend: 'stable', churn: 'lt5', customerCount: 'm100_1k', concentration: 'lt10', traffic: ['seo', 'wom'], teamSize: 'solo', handover: 'full', transferAssets: ['domain', 'emaillist'], legalEntity: 'llc', urgency: 'norush', whySelling: 'pivoting', }; const synth = window.__willisellitSynth(answers, false); console.log(synth.buyer.name); // "Portfolio Acquirer" console.log(synth.v.line); // "Will sell — clean process likely" console.log(synth.r.low, synth.r.high); // 748000 1944000 console.log(synth.pl.list[0].name); // "FE International" console.log(synth.story.fullNarrative); // concatenated story string
2.2 Cascade vs modular viewport
// Cascade mode — denser narrative (top-3 determinants instead of top-2) const synth = window.__willisellitSynth(answers, false, { viewport: 'cascade' });
3. Input: answer schema
The answers object is a flat key-value map. Two paths exist:
Revenue path (isPrerev = false)
| Key | Type | Required | Allowed values |
|---|---|---|---|
| asset | string | yes | saas · ai · mobile · ecom · newsletter · ext · shopify · wordpress · api · marketplace · telegram · discord · chatbot · course · template · content · podcast · youtube · service · utility · email · hybrid · game · other |
| industry | string | yes | One of 20 categories: AI / ML, FinTech, SaaS / Productivity, EdTech, etc. |
| audience | string | yes | enterprise · b2b · smb · solo · b2c · b2b2c · niche |
| stage | string | yes | early · est · growth · mature |
| revenueType | string | yes | recurring · usage · mixed · onetime · ads |
| mrr | string | yes | Numeric string. Monthly revenue (annualized ÷ 12 for non-recurring). |
| margin | string | yes | lt30 · m30_50 · m50_70 · gt70 · unsure |
| trend | string | yes | grow · stable · decline · new |
| churn | string | yes | lt5 · m510 · gt10 · unsure · na |
| customerCount | string | yes | lt10 · m10_100 · m100_1k · gt1k · many |
| concentration | string | conditional* | lt10 · m1025 · gt25 |
| traffic | string[] | yes | Multi-select: seo · paid · social · email · wom · direct · appstore · platform · integ · other |
| teamSize | string | yes | solo · m1_3 · m4_10 · gt10 · contract |
| handover | string | yes | full · partial · mixed · none |
| transferAssets | string[] | yes | Multi-select: domain · emaillist · socials · content · inventory · ip · contracts · data |
| legalEntity | string | yes | llc · ccorp · scorp · soleprop · noentity · other |
| urgency | string | yes | norush · quick · asap |
| whySelling | string | yes | pivoting · burnout · cashneeded · opportunistic · lifechange |
* concentration is skipped when audience is b2c AND customerCount is many, OR when revenueType is ads.
Pre-revenue path (isPrerev = true)
| Key | Type | Required | Allowed values |
|---|---|---|---|
| projectType | string | yes | Same as asset in revenue path |
| industry | string | yes | Same as revenue path |
| audience | string | yes | Same as revenue path |
| buildStage | string | yes | wireframe · mvp · closedbeta · live · pivoting |
| userTier | string | yes | none · handful · early · growing · substantial · massive |
| userEngage | string | conditional* | paying · active · signups · waitlist |
| growth | string | conditional** | g30 · g15 · g5 · flat |
| demand | string | yes | press · big · some · none |
| competition | string | yes | greenfield · few · crowded · saturated |
| codeQuality | string | yes | prod · solid · basic · proto · nocode |
| teamSize | string | yes | Same as revenue path |
| urgency | string | yes | Same as revenue path |
| whySelling | string | yes | Same as revenue path |
* userEngage auto-fills to "waitlist" when userTier is "none".
** growth is skipped when userTier is "none" (defaults to "flat").
4. caseSynthesis()
stable
function caseSynthesis( answers: object, isPrerev: boolean, opts?: { viewport?: 'modular' | 'cascade' } ): SynthesisResult
Parameters
answers— flat key-value map matching the schema in section 3.isPrerev—truefor pre-revenue path,falsefor revenue path. Determines which schema and pipeline applies.opts.viewport—'modular'(default) or'cascade'. Cascade returns top-3 determinants instead of top-2 and adjusts narrative voice for sequential reading.
Returns
A SynthesisResult object — fully described in sections 5 and 6.
Behavior
- Pure: same inputs always yield identical outputs.
- Synchronous: no async / no I/O.
- Defensive: invalid
opts(non-object) triggers a console warning and falls back to defaults. - Deep-cloned profiles: mutating
synth.buyer.wantsetc. does not affect subsequent calls.
5. Output: synth shape
interface SynthesisResult { r: { low: number, // low estimate in USD mid: number, // midpoint estimate high: number, // high estimate score: number, // clamped multiplier product (0.30–3.00) rawScore: number, // unclamped diagnostic value mrr: number, // parsed monthly revenue input flags: Flag[], // combo-flag list — see Section 6 annualBasisDiv: number, // 1.0 default; 2.2 if asset-revenue mismatch isPrerev: boolean, }, v: { tier: 'go' | 'caution' | 'stop', line: string, // short verdict headline explain: string, // 1–2 sentence explanation }, hf: { helps: FixItem[], // positive items ("your churn is great") fix: FixItem[], // negative items, sorted by lift descending }, pl: { list: Marketplace[], // 18 marketplaces sorted by fit; primary at [0] topReason: string // human-readable explanation of why this list }, advice: { s: string, t: string }[], // 4 tailored tips (subject + body) buyer: { id: ArchetypeId, // 'institutional' | 'strategic' | 'portfolio' | ... name: string, // "Institutional Investor", "Portfolio Acquirer", ... who: string, // 1–2 sentence buyer description wants: string[], // 3 things they want pays_for: string[], // 2–3 things they'll pay extra for discount_for: string[], // 2 things they'll discount for timeline: string, // e.g. "3–6 weeks. They've done this before." lead_with: string, // 1-sentence outreach guidance determinants: Determinant[], // signals that drove archetype pick confidence: { label: 'high' | 'medium' | 'low', score: number, // 0.0–1.0 completeness: number, contradictions: number, archetypeMatch: number, }, }, firstMove: { action: string, // full action sentence actionShort: string, // imperative verb-phrase reason: string, // archetype-templated full reason reason_short: string, // 1-sentence reason for cascade lift: number, // 0.0–1.0 estimated impact fixKey: string | null, // answer field this fix targets confidence: {...}, // same shape as buyer.confidence }, flags: Flag[], // alias of r.flags isPrerev: boolean, viewport: 'modular' | 'cascade', story: Story, // see Section 6 } interface Determinant { key: string, // internal field name (e.g. "churn") label: string, // human-readable ("Retention") value: any, // raw value (e.g. "lt5", 21600000) weight: number, // 0.0–1.0 relative importance note: string, // full-sentence explanation } interface FixItem { t: string, // full description text i: string, // short impact label (e.g. "−15%") fixKey?: string, // answer field this fix targets fixValue?: any, // answer value to apply when "fixed" lift_estimate: number, // 0.0–1.0 estimated multiplier impact archetypeReason: string, // archetype-templated explanation (may be empty) copy?: string, // listing-ready brag sentence (helps items only) } interface Flag { key: string, // internal flag id (e.g. "asset_revtype_mismatch") label: string, // human-readable ("Asset / revenue mismatch") penalty: number, // multiplicative score penalty (e.g. 0.85) reason: string, // full-sentence explanation } interface Marketplace { id: string, // 'feinternational', 'acquire', ... name: string, // "FE International" link: string, // canonical URL fit: number, // fit score (post-archetype-bias) } type ArchetypeId = | 'institutional' | 'strategic' | 'portfolio' | 'first_time' | 'indie' | 'hobbyist';
6. Output: synth.story shape
The story object is the cascade-narrative-ready, sectioned representation of the synthesis. Use this when rendering a long-form report or consultation-style page.
interface Story { headline: string, // "Portfolio-Acquirer case at $748K–$1.9M" opener: string, // 1-sentence opening line (hedge-aware) archetype: { paragraph: string, // 2–3 sentence "why this archetype" paragraph determinants: Determinant[], // top-2 (modular) or top-3 (cascade) sectionCta: { text: string, href: string }, }, range: string, // "Range: $748K–$1.9M (mid $1.3M)." fix: { wrapper: string, // "For them, the highest-leverage step is to..." actionShort: string, // "Multi-list with a deadline" action: string, // full action sentence reason: string, // archetype-templated full reason reason_short: string, // 1-sentence reason lift: number, // 0.0–1.0 liftPct: string, // "+10%" fixKey: string | null, confidence: {...}, sectionCta: { text: string, href: string }, }, paysFor: string[], // top-2 buyer.pays_for items platform: string, // "Top-fit marketplace for portfolio acquirers: FE..." closer: string, // verdict-tier-aware closing sentence cta: { text: string, href: string }, // final call-to-action sections: TocEntry[], // table-of-contents (only sections that render) sectionData: object, // reserved hook for future cascade-side overrides inputsSummary: InputPair[], // "you said: X, Y, Z" sticky-sidebar pairs fullNarrative: string, // concatenated story for analytics / share-text } interface TocEntry { id: string, // "verdict" | "buyer" | "fixes" | "next" | "platforms" | "advice" label: string, // human-readable section label anchor: string, // "#verdict" — match DOM anchor on rendered page } interface InputPair { key: string, // internal answer field name label: string, // "Audience" value: string, // human-readable resolved value ("SMB", "$120K") raw: any, // original answer value }
7. Render helpers
stable
Three HTML-string builders are exposed under window.__willisellitRender. They return ready-to-mount HTML using the .sl-block / .first-move / .bp-determinants CSS classes from the willisellit stylesheet.
window.__willisellitRender.archetype(archetype: Story['archetype']): string window.__willisellitRender.flags(flags: Flag[]): string window.__willisellitRender.fixCard(fix: Story['fix']): string
All helpers HTML-escape user-provided text via an internal _esc() utility.
8. Self-test & CI
stable
window.__willisellitSelfTest(opts?: { silent?: boolean }): SelfTestSummary interface SelfTestSummary { passed: number, total: number, results: { name: string, ok: boolean, archetypeOk: boolean, shapeOk: boolean, extraOk: boolean, expected: ArchetypeId, got: ArchetypeId, score: number, range: string, confidence: string, firstMove: string, flags: number, }[], }
Behavior
- Runs 15 representative fixtures covering all six archetypes, edge cases, viewport modes, and shape assertions.
- Throws in node-context (no
document.body) when at least one fixture fails — CI-friendly default. - Never throws in browser context. Failures surface only via console.
- Pass
{silent: true}to suppress throw even in node-context.
Example: CI integration (Node)
// node-runner.js — exits non-zero if any fixture fails const fs = require('fs'); const path = require('path'); const src = fs.readFileSync('index.html', 'utf8'); const body = src.match(/<script>([\s\S]*?)<\/script>/)[1]; // Stub minimal DOM — see willisellit-test-utils for a complete shim global.window = {}; global.document = { body: null, getElementById: () => null }; // ... other DOM stubs new Function(body)(); try { const summary = window.__willisellitSelfTest(); console.log(summary.passed, '/', summary.total); } catch (e) { process.exit(1); }
9. Examples
9.1 Render a custom result panel
const synth = window.__willisellitSynth(answers, false); const r = window.__willisellitRender; document.getElementById('output').innerHTML = '<h2>' + synth.story.headline + '</h2>' + '<p>' + synth.story.opener + '</p>' + r.archetype(synth.story.archetype) + r.flags(synth.flags) + r.fixCard(synth.story.fix);
9.2 Extract just the valuation range
const { low, mid, high } = window.__willisellitSynth(answers, false).r; console.log(`Range: $${low}–$${high}, mid $${mid}`);
9.3 Get just the buyer archetype + reasoning
const { buyer } = window.__willisellitSynth(answers, false); console.log('Archetype:', buyer.name); buyer.determinants.forEach(d => console.log(d.label, ':', d.note));
9.4 Get top marketplace recommendation only
const top = window.__willisellitSynth(answers, false).pl.list[0]; console.log(top.name, '→', top.link);
10. Licensing & commercial use
What constitutes commercial use
- Embedding the engine in a paid product or service
- Reselling valuation outputs as a paid service
- Using outputs in M&A advisory work charged to clients
- White-labeling the engine under your own brand
What is allowed without a commercial license
- Non-commercial educational use
- Research and benchmarking against your own data
- Internal tools at your company (not customer-facing)
- Open-source projects with a compatible license
Attribution requirement
Any integration must include a visible attribution: "Valuation methodology powered by willisellit.com" with a link to https://willisellit.com/methodology.html. The link must be discoverable (not hidden, not rel="nofollow") on any page where outputs are shown.
Disclaimer of warranty
The synthesis engine is provided "as is" with no warranty of fitness for any particular purpose. Outputs are estimates calibrated against historical data; they should not be construed as financial advice or guarantees of sale price. Acquirers always have the final word on price.
Contact
Questions, custom integrations, commercial licensing, or methodology disagreements: [email protected]. Response time typically 2–5 business days.
Last revised 2026-05 · v0.6 · The willisellit synthesis engine is independent and not affiliated with any specific marketplace.