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:

SymbolTypePurpose
window.__willisellitSynth(answers, isPrerev, opts?)functionMain entry point. Returns a complete synthesis object.
window.__willisellitSelfTest(opts?)functionRuns 15 self-test fixtures. Returns a summary object.
window.__willisellitRenderobjectHTML 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.).

Stability commitment The shape of 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)

KeyTypeRequiredAllowed values
assetstringyessaas · ai · mobile · ecom · newsletter · ext · shopify · wordpress · api · marketplace · telegram · discord · chatbot · course · template · content · podcast · youtube · service · utility · email · hybrid · game · other
industrystringyesOne of 20 categories: AI / ML, FinTech, SaaS / Productivity, EdTech, etc.
audiencestringyesenterprise · b2b · smb · solo · b2c · b2b2c · niche
stagestringyesearly · est · growth · mature
revenueTypestringyesrecurring · usage · mixed · onetime · ads
mrrstringyesNumeric string. Monthly revenue (annualized ÷ 12 for non-recurring).
marginstringyeslt30 · m30_50 · m50_70 · gt70 · unsure
trendstringyesgrow · stable · decline · new
churnstringyeslt5 · m510 · gt10 · unsure · na
customerCountstringyeslt10 · m10_100 · m100_1k · gt1k · many
concentrationstringconditional*lt10 · m1025 · gt25
trafficstring[]yesMulti-select: seo · paid · social · email · wom · direct · appstore · platform · integ · other
teamSizestringyessolo · m1_3 · m4_10 · gt10 · contract
handoverstringyesfull · partial · mixed · none
transferAssetsstring[]yesMulti-select: domain · emaillist · socials · content · inventory · ip · contracts · data
legalEntitystringyesllc · ccorp · scorp · soleprop · noentity · other
urgencystringyesnorush · quick · asap
whySellingstringyespivoting · 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)

KeyTypeRequiredAllowed values
projectTypestringyesSame as asset in revenue path
industrystringyesSame as revenue path
audiencestringyesSame as revenue path
buildStagestringyeswireframe · mvp · closedbeta · live · pivoting
userTierstringyesnone · handful · early · growing · substantial · massive
userEngagestringconditional*paying · active · signups · waitlist
growthstringconditional**g30 · g15 · g5 · flat
demandstringyespress · big · some · none
competitionstringyesgreenfield · few · crowded · saturated
codeQualitystringyesprod · solid · basic · proto · nocode
teamSizestringyesSame as revenue path
urgencystringyesSame as revenue path
whySellingstringyesSame 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.
  • isPrerevtrue for pre-revenue path, false for 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.wants etc. 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

License terms The methodology document is published under CC BY 4.0. The synthesis algorithm is available under Apache 2.0 for non-commercial integration. Commercial integrations (paid SaaS products, valuation services charging end users, M&A advisory tools) require a separate commercial license — contact [email protected].

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.