Programmatic scoring for partners.
One POST, one JSON scorecard. The same deterministic 10-axis engine that powers the on-site /score form, returned as a structured response your app can render or route without setting up a separate scoring stack.
Get a key
One key per integration. Plaintext shown once.
We provision keys by hand on a per-partner basis so we know who's calling the engine. Ask us for one by email — once issued we run the deploy-time seed and your plaintext lands in the server log exactly once, in this format:
Server log (first boot after the key is seeded)
════════════════════════════════════════════════════════════════ /api/v1/score: seeded first ApiKey (label=partner-default) plaintext : hlms_<48 hex chars> Copy it now — it will not be shown again. Rotate by setting ApiKey.revokedAt = now() on the old row, then deleting it so the next seed run generates a fresh plaintext. ════════════════════════════════════════════════════════════════
Rotate by setting revokedAt on the current row (the endpoint will start returning 401), then deleting the row so the next deploy re-seeds with a fresh plaintext.
Request
One endpoint. POST only.
POST /api/v1/score with a JSON body of exactly { "input": "…" }. Unknown keys (including email) are rejected — the schema is strict.
Sample request
curl --request POST 'https://helmslee.polsia.app/api/v1/score' \
--header 'Content-Type: application/json' \
--header 'x-api-key: hlms_<your-key-here>' \
--data '{ "input": "A vertical SaaS for independent venue owners running weekly open mics — scheduling, payouts, and a public lineup page." }'Response
One composite score, ten axes, four winnability signals.
score is 0–100. breakdown.axes is the same ten-axis scorecard as on-site. recommendations surfaces the strongest "improve this" signals only. No idea text, no slug, no timestamp, no email — the response is safe to log and cache.
Sample response (200)
{
"score": 62,
"breakdown": {
"axes": [
{
"key": "demand",
"label": "Demand",
"score": 8,
"verdict": "pass",
"rationale": "Search interest has held for 12+ months or names a clear buyer.",
"sourceUrl": "https://trends.google.com/"
},
{
"key": "competition",
"label": "Competition",
"score": 6,
"verdict": "neutral",
"rationale": "Existing tools validate the category, but the wedge is still specific.",
"sourceUrl": "https://www.g2.com/"
},
{
"key": "profitability",
"label": "Profitability",
"score": 7,
"verdict": "pass",
"rationale": "A recurring workflow creates a clear path to paid operational value.",
"sourceUrl": "https://www.bls.gov/ooh/"
},
{
"key": "scalability",
"label": "Scalability",
"score": 8,
"verdict": "pass",
"rationale": "Software can serve more venues without matching growth in labor.",
"sourceUrl": "https://www.census.gov/programs-surveys/economic-census.html"
},
{
"key": "automation",
"label": "Automation",
"score": 7,
"verdict": "pass",
"rationale": "Scheduling and payout coordination are repeatable, rules-based tasks.",
"sourceUrl": "https://www.nist.gov/"
},
{
"key": "retention",
"label": "Retention",
"score": 6,
"verdict": "neutral",
"rationale": "Weekly operating workflows can retain users if the lineup becomes habitual.",
"sourceUrl": "https://www.sba.gov/business-guide/manage-your-business"
},
{
"key": "seo",
"label": "SEO surface",
"score": 5,
"verdict": "neutral",
"rationale": "Venue and event pages create useful local search surfaces over time.",
"sourceUrl": "https://developers.google.com/search/docs"
},
{
"key": "virality",
"label": "Virality",
"score": 4,
"verdict": "fail",
"rationale": "The workflow is shareable, but each venue may not invite many new users.",
"sourceUrl": "https://www.pewresearch.org/internet/"
},
{
"key": "startupCost",
"label": "Startup cost",
"score": 7,
"verdict": "pass",
"rationale": "The first version can launch with standard scheduling and payment tooling.",
"sourceUrl": "https://www.sba.gov/business-guide/plan-your-business"
},
{
"key": "technicalDifficulty",
"label": "Technical difficulty",
"score": 6,
"verdict": "neutral",
"rationale": "Payments and scheduling add edge cases, but the core model is conventional.",
"sourceUrl": "https://developer.mozilla.org/en-US/docs/Web/API"
}
],
"winnabilityScore": 64,
"winnabilityBreakdown": {
"signals": [
{
"key": "incumbentStrength",
"label": "Incumbent strength",
"score": 6,
"verdict": "neutral"
},
{
"key": "switchingCost",
"label": "Switching cost",
"score": 5,
"verdict": "neutral"
},
{
"key": "asymmetricAdvantage",
"label": "Asymmetric advantage",
"score": 7,
"verdict": "pass"
},
{
"key": "structuralLiability",
"label": "Structural liability",
"score": 6,
"verdict": "neutral"
}
],
"score": 64,
"verdict": "neutral"
}
},
"recommendations": [
{
"axis": "virality",
"verdict": "fail",
"message": "Improve `Virality` — this axis is below the pass bar."
},
{
"axis": "winnability.switchingCost",
"verdict": "neutral",
"message": "Sharpen `Switching cost` — score 5/10."
}
]
}Rate limit
60 requests per key per minute.
Sliding-window, in-process for the first slice (easy to swap for Redis later — the public signature in src/lib/api/rate-limit.ts is the swap point). Two headers surface on every 200 response:
On exhaustion we return 429 with Retry-After (seconds) and the same reset header. The limit is per-key, not per-IP, so partners behind NAT share one budget — issue more keys if you need more parallelism.
Errors
400. 401. 429. That's it.
Every error response is JSON. Look at error or errors — headers carry the reset time on 429.
When
Body is not JSON, `input` is missing/empty, or unknown keys are present.
Fix
Send exactly `{ "input": "your idea" }`. The endpoint rejects any other field.
When
No `x-api-key` header, header is empty, the digest does not match a row, or the row is revoked.
Fix
Re-check the header. If the key was revoked, request a new one — old plaintexts are not re-derivable.
No or empty `x-api-key`
{ "error": "missing api key" }Unknown or revoked key
{ "error": "invalid or revoked api key" }When
More than 60 requests in a 60-second window for the same key.
Fix
Wait for `Retry-After` seconds, then resume. The budget is per-key, so spread across keys if you need more throughput.
Need a key or a quota bump?
Email us. We provision by hand.
We track every integration on a per-partner basis so abuse is contained and we know what wedges the engine is hitting. Drop us a line with the volume you expect and a description of the surface you'll be wiring into.