Fidelity API
Submit a lyric plus the brief it was written for. Get back a 7-component score on whether the lyric actually answers the brief — an axis orthogonal to the 12-metric quality rubric. Powered by the open Fidelity Standard v0.1.0 (CC BY 4.0).
sfai_live_… keys that work against /api/v1/score.Quality measures “is this a good song.” Fidelity measures “did this answer the question I asked?”
A song can score A+ on quality + F on fidelity (gorgeous, wrong prompt) or C+ on quality + A+ on fidelity (dutiful but bland). Both happen routinely. The 12-metric Lyric Scoring Standard has measured the first axis since 2024. Fidelity is the second axis — designed to be queried separately, with its own grade, weighting, and component breakdown.
On SongForgeAI’s public leaderboard, songs rank by a composite of 0.6 × quality + 0.4 × fidelity. B2B partners building lyric pipelines typically score both and gate on either independently.
The 7 components
Each component scores 0–100 and contributes to the composite with the weight below. Full mathematical definitions live in the public standard; the API returns both the composite and per-component breakdown so consumers can render their own per-axis UI.
| Component | Weight | What it asks |
|---|---|---|
premisePremise match | 20% | Did the lyric serve the central premise the brief named? |
anchorCoverageAnchor coverage | 20% | Did each named anchor surface, and surface concretely? |
structureStructure compliance | 15% | Does the section order match the brief’s structure array? |
styleStyle adherence | 15% | Did declared style constraints (concrete, plainspoken, etc.) hold? |
forbiddenForbidden language | 10% | Did the lyric avoid the brief’s forbidden words/phrases? |
chorusEvolutionChorus evolution | 10% | Do successive chorus passes earn their repetition? |
transcendenceEarned transcendence | 10% | Are big-claim lines paid for by specific images that preceded them? |
Endpoint
Bearer-auth via Authorization: Bearer sfai_live_…. Body is JSON. Response is JSON. Wall-clock latency: ~2–4s with the default Haiku premise audit, <50ms with skipPremiseAudit: true.
Example: structured brief
The preferred call shape. You supply a structured brief object; the endpoint scores the lyric against each named constraint and returns a per-component breakdown.
curl -X POST https://songforgeai.com/api/v1/fidelity \
-H "Authorization: Bearer sfai_live_<YOUR_KEY>" \
-H "Content-Type: application/json" \
-d '{
"lyrics": "[VERSE 1]\nThe water tower leans against the sky\n...",
"brief": {
"premise": "a song about a dying town and a missing mother",
"anchors": ["water tower", "mother", "town"],
"structure": ["verse", "chorus", "verse", "chorus", "bridge", "chorus"],
"styleConstraints": ["concrete", "plainspoken"],
"forbiddenLanguage": ["heart", "soul"],
"constraintMode": "strict"
}
}'Response
{
"score": 84,
"grade": "B+",
"verdict": "All anchors landed; one structural or style miss.",
"phase": "phase-2",
"auditVersion": "1.5.0",
"complexity": 6,
"complexityBucket": "primary",
"constraintMode": "standard",
"componentScores": {
"premise": 80,
"anchorCoverage": 88,
"structure": 90,
"style": 82,
"forbidden": 100,
"chorusEvolution": 75,
"transcendence": 70
},
"audits": {
"structure": { "score": 90, "passed": true },
"anchors": { "score": 88, "totalAnchors": 3, "hitAnchors": 3 },
"thesis": { "score": 85 },
"specificity": { "score": 82 },
"forbidden": { "score": 100, "violations": [] },
"chorusEvolution":{ "score": 75 },
"transcendence": { "score": 70 },
"premise": { "verdict": "on-brief", "score": 92, "state": "judged" }
},
"seal": {
"standardVersion": "v0.1.0",
"auditVersion": "1.5.0",
"build": 2830,
"buildSha": "..."
},
"generatedAt": "2026-05-20T12:34:56Z"
}Example: no structured brief
When you only have the raw prompt the writer typed (no structured anchors / structure / style constraints), send it as rawPrompt and the endpoint extracts a permissive brief on its own. Most audits still fire; the premise component returns null when no anchors exist to ground it.
// When you don't have a structured brief, send rawPrompt and
// the endpoint extracts a permissive brief on its own. The
// premise component returns null in this mode (no anchors
// to ground the audit), but structure / style / forbidden
// language audits all still fire.
{
"lyrics": "...",
"rawPrompt": "write a country song about leaving a small town",
"skipPremiseAudit": true
}Node / TypeScript
const res = await fetch('https://songforgeai.com/api/v1/fidelity', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SFAI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
lyrics: draftLyrics,
brief: {
premise: 'a song about a dying town',
anchors: ['water tower', 'mother'],
constraintMode: 'standard',
},
// Skip the Haiku premise audit to keep the call pure-sync (<50ms).
skipPremiseAudit: true,
}),
});
const data = await res.json();
console.log(`Fidelity ${data.score} (${data.grade}) — ${data.verdict}`);Error contract
| Status | error | When |
|---|---|---|
| 400 | invalid_body | Body failed Zod validation (missing lyrics, bad enum, oversize string, etc.) |
| 401 | missing_api_key · invalid_api_key_format · unknown_api_key · api_key_revoked | No Authorization header, malformed token, unknown hash, or revoked key |
| 500 | fidelity_audit_failed | Audit orchestrator threw; detail field carries the message |
| 503 | service_not_configured | Supabase service role not configured at deploy time |
Every response includes a seal object with the standard version, audit version, build number, and git SHA. Store the seal alongside the score so you can prove which version of the standard scored your lyric — the same posture SongForgeAI takes on its public leaderboard.
"seal": {
"standardVersion": "v0.1.0",
"auditVersion": "1.5.0",
"build": 2830,
"buildSha": "fcb47e04d..."
}Next
Mathematical definitions, the 7 components in full, the rationale, and the CC BY 4.0 license.
OpenAPI 3.0 reference for every endpoint. Download the raw spec at /api/v1/openapi.json.
The first axis: /api/v1/score against the 12-metric quality rubric.
Comment on the standard before 2026-05-26. Anything we change gets a numbered version bump.