File size: 43,894 Bytes
7c184ea def40d7 2bc9fc3 def40d7 2bc9fc3 def40d7 2bc9fc3 def40d7 2bc9fc3 def40d7 2bc9fc3 def40d7 2bc9fc3 def40d7 7c184ea | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>FDA Establishment Registration XML — check registration xml free</title>
<meta name="description" content="This browser tool checks FDA SPL establishment registration XML for DUNS format, LOINC document type, UUID setId, and the annual registration window.">
<style>
:root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512}
.wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px}
textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box}
.bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer}
input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px}
table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65}
.sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600}
.note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa}
</style>
<script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
<!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/spl-registration-lint"></head>
<body>
<div class="wrap">
<h1>FDA Establishment Registration XML — check registration xml free</h1>
<p class="lede">Lints FDA SPL establishment registration XML before it goes to the FDA ESG: DUNS format, LOINC document type, UUID setId, and the October 1 - December 31 annual registration window. Runs entirely in your browser — nothing is uploaded.</p>
<textarea id="src" spellcheck="false"><?xml version="1.0" encoding="UTF-8"?>
<document xmlns="urn:hl7-org:v3">
<id root="c7e4b118-2f90-4a63-b8d2-53a9c6e1d077"/>
<code code="34391-3" codeSystem="2.16.840.1.113883.6.69" displayName="HUMAN PRESCRIPTION DRUG LABEL"/>
<title>ESTABLISHMENT REGISTRATION</title>
<effectiveTime value="20251110"/>
<setId root="c7e4b118-2f90-4a63-b8d2-53a9c6e1d077"/>
<versionNumber value="0"/>
<author>
<assignedEntity>
<representedOrganization>
<id extension="12-345-6789" root="1.3.6.1.4.1.519.1"/>
<name>Harbor Ridge Pharma LLC</name>
<assignedEntity>
<assignedOrganization>
<assignedEntity>
<assignedOrganization>
<id extension="98765432" root="1.3.6.1.4.1.519.1"/>
<name>Harbor Ridge Pharma LLC - Building 2</name>
<addr>
<streetAddressLine>4120 Harbor Ridge Road</streetAddressLine>
<city>Trenton</city>
<state>NJ</state>
<postalCode>08611</postalCode>
<country>USA</country>
</addr>
</assignedOrganization>
<performance>
<actDefinition>
<code code="C43360" codeSystem="2.16.840.1.113883.3.26.1.1" displayName="MANUFACTURE"/>
</actDefinition>
</performance>
</assignedEntity>
</assignedOrganization>
</assignedEntity>
</representedOrganization>
</assignedEntity>
</author>
</document></textarea>
<div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div>
<div id="out" aria-live="polite"></div>
<div id="paid"><strong>Free here:</strong> Lint one SPL registration XML file in the editor and get every blocking error with its line number.. <strong>In the editor:</strong> Lint every SPL file in the submission folder at once and export a dated readiness report you keep. — that part asks for a licence key.</div>
<p id="getfull"><a href="https://getreadystack.com/api/buy/cl/polar_cl_5DGxPviszTwjOqxMnoZEuBHdaWberjjix1mAZ1mQljU">Full version - $29 once: sweep every SPL file in the submission folder and export a dated readiness report</a> · one licence key per person or CI seat · A regulatory-affairs consultant bills $100-$200 per hour.</p>
<p class="note">Same engine as the VS Code extension, byte for byte.</p>
</div>
<script>var SPL_RULES = [
{"id": "doc_type", "sev": "err", "title": "Document type is not ESTABLISHMENT REGISTRATION", "fix": "Set the document \u003ccode\u003e to code=\"51725-0\" (LOINC ESTABLISHMENT REGISTRATION)."},
{"id": "loinc_system", "sev": "err", "title": "Document code is not declared as LOINC", "fix": "Set codeSystem=\"2.16.840.1.113883.6.1\" on the document \u003ccode\u003e."},
{"id": "doc_uuid", "sev": "err", "title": "Document id or setId is not a distinct UUID", "fix": "Give \u003cid\u003e a fresh UUID for this version and keep \u003csetId\u003e the stable UUID of the registration; they must never be equal."},
{"id": "version_number", "sev": "err", "title": "versionNumber is missing or not a positive integer", "fix": "Set \u003cversionNumber value=\"1\"/\u003e on a first submission and raise it by one on every resubmission."},
{"id": "effective_date", "sev": "err", "title": "effectiveTime is not a valid YYYYMMDD date", "fix": "Write effectiveTime as eight digits, for example value=\"20261001\"."},
{"id": "reg_window", "sev": "err", "title": "effectiveTime falls outside the October 1 - December 31 registration window", "fix": "Date the annual registration inside the October 1 - December 31 window of the year you are registering for."},
{"id": "lapse_clock", "sev": "err", "title": "Registration is for an earlier year and has already lapsed", "fix": "Re-date and resubmit the registration inside this year's October 1 - December 31 window."},
{"id": "duns_root", "sev": "err", "title": "No D-U-N-S number is carried on any organization", "fix": "Add \u003cid extension=\"123456789\" root=\"1.3.6.1.4.1.519.1\"/\u003e to the registrant and to every establishment."},
{"id": "duns_format", "sev": "err", "title": "D-U-N-S number is not exactly nine digits", "fix": "Write the D-U-N-S number as nine digits with no dashes, spaces or leading letters."}
]
;</script>
<script>// SPL Registration Lint - engine. Same file runs in Node and in the browser.
var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.SPL_RULES;
var DUNS_ROOT = '1.3.6.1.4.1.519.1';
var LOINC = '2.16.840.1.113883.6.1';
var REG_CODE = '51725-0';
var UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
function byId(id) {
for (var i = 0; i < RULES.length; i++) { if (RULES[i].id === id) return RULES[i]; }
return {id: id, sev: 'err', title: id, fix: ''};
}
function lineOf(text, idx) {
if (idx < 0) return 1;
return text.slice(0, idx).split('\n').length;
}
function attr(tag, name) {
var m = tag.match(new RegExp(name + '\\s*=\\s*"([^"]*)"'));
return m ? m[1] : null;
}
// Everything before the first <author> is the SPL document header.
function headerOf(text) {
var i = text.indexOf('<author');
return i === -1 ? text : text.slice(0, i);
}
function findTag(region, name) {
var m = region.match(new RegExp('<' + name + '\\b[^>]*>'));
return m ? {tag: m[0], idx: m.index} : null;
}
function isRealDate(v) {
if (!/^[0-9]{8}$/.test(v)) return false;
var y = +v.slice(0, 4), mo = +v.slice(4, 6), d = +v.slice(6, 8);
if (mo < 1 || mo > 12 || d < 1) return false;
var last = new Date(Date.UTC(y, mo, 0)).getUTCDate();
return d <= last;
}
function daysBetween(a, b) {
return Math.round((b - a) / 86400000);
}
function check(text, opts) {
opts = opts || {};
var today = opts.today || '2026-09-21';
var todayYear = +today.slice(0, 4);
var findings = [];
function add(id, msg, line) {
var r = byId(id);
findings.push({check: id, sev: r.sev, msg: msg, line: line});
}
var head = headerOf(text);
var headLines = head.split('\n').length;
// --- document <code> ---
var code = findTag(head, 'code');
if (!code) {
add('doc_type', 'No document <code> element. ' + byId('doc_type').fix, 1);
} else {
var ln = lineOf(text, code.idx);
var cv = attr(code.tag, 'code');
var cs = attr(code.tag, 'codeSystem');
if (cv !== REG_CODE) {
add('doc_type', 'Document type is "' + (cv || '(none)') + '", not ' + REG_CODE + '. ' + byId('doc_type').fix, ln);
}
if (cs !== LOINC) {
add('loinc_system', 'codeSystem is "' + (cs || '(none)') + '", not the LOINC OID ' + LOINC + '. ' + byId('loinc_system').fix, ln);
}
}
// --- <id> and <setId> ---
var did = findTag(head, 'id');
var sid = findTag(head, 'setId');
var dRoot = did ? attr(did.tag, 'root') : null;
var sRoot = sid ? attr(sid.tag, 'root') : null;
if (!did || !UUID_RE.test(dRoot || '')) {
add('doc_uuid', 'Document <id root> is "' + (dRoot || '(missing)') + '", which is not a UUID. ' + byId('doc_uuid').fix, did ? lineOf(text, did.idx) : 1);
}
if (!sid || !UUID_RE.test(sRoot || '')) {
add('doc_uuid', 'Document <setId root> is "' + (sRoot || '(missing)') + '", which is not a UUID. ' + byId('doc_uuid').fix, sid ? lineOf(text, sid.idx) : 1);
} else if (dRoot && sRoot.toLowerCase() === dRoot.toLowerCase()) {
add('doc_uuid', '<setId> repeats the <id> UUID, so the FDA ESG reads this as a new registration instead of a new version. ' + byId('doc_uuid').fix, lineOf(text, sid.idx));
}
// --- <versionNumber> ---
var ver = findTag(head, 'versionNumber');
var vv = ver ? attr(ver.tag, 'value') : null;
if (!ver || !/^[1-9][0-9]*$/.test(vv || '')) {
add('version_number', 'versionNumber is "' + (vv === null ? '(missing)' : vv) + '", not a positive integer. ' + byId('version_number').fix, ver ? lineOf(text, ver.idx) : 1);
}
// --- <effectiveTime> : format, window, lapse ---
var eff = findTag(head, 'effectiveTime');
var ev = eff ? attr(eff.tag, 'value') : null;
var effLine = eff ? lineOf(text, eff.idx) : 1;
if (!eff || !isRealDate(ev || '')) {
add('effective_date', 'effectiveTime is "' + (ev === null ? '(missing)' : ev) + '", not a valid YYYYMMDD date. ' + byId('effective_date').fix, effLine);
} else {
var y = +ev.slice(0, 4), mo = +ev.slice(4, 6);
if (mo < 10) {
add('reg_window', 'effectiveTime ' + ev + ' is month ' + mo + ', before the October 1 - December 31 annual registration window. ' + byId('reg_window').fix, effLine);
}
if (y < todayYear) {
var lapsed = daysBetween(new Date(y + '-12-31T00:00:00Z'), new Date(today + 'T00:00:00Z'));
add('lapse_clock', 'This registration is dated ' + ev + ', so it expired on ' + y + '-12-31, ' + lapsed + ' days ago. ' + byId('lapse_clock').fix, effLine);
}
}
// --- D-U-N-S numbers on every organization ---
var idTags = text.match(/<id\b[^>]*\/?>/g) || [];
var dunsSeen = 0;
var cursor = 0;
for (var i = 0; i < idTags.length; i++) {
var t = idTags[i];
var at = text.indexOf(t, cursor);
cursor = at + 1;
if (attr(t, 'root') !== DUNS_ROOT) continue;
dunsSeen++;
var ext = attr(t, 'extension');
if (!/^[0-9]{9}$/.test(ext || '')) {
add('duns_format', 'D-U-N-S number "' + (ext === null ? '(missing)' : ext) + '" is not nine digits. ' + byId('duns_format').fix, lineOf(text, at));
}
}
if (dunsSeen === 0) {
add('duns_root', 'No <id root="' + DUNS_ROOT + '"> anywhere in the file. ' + byId('duns_root').fix, headLines);
}
findings.sort(function (a, b) { return a.line - b.line; });
return {findings: findings};
}
var API = {engine: {check: check}, RULES: RULES, RULE_COUNT: RULES.length};
if (typeof module !== 'undefined') { module.exports = API; }
if (typeof window !== 'undefined') { window.SPLENGINE = API; }
</script>
<script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다.
'use strict';
function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); }
function summaryLine(res) {
const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length;
return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.';
}
function toText(res, label) {
const f = (res && res.findings) || [];
const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)];
f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); });
return lines.join('\n');
}
function toHtml(res) {
const f = (res && res.findings) || [];
if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>';
const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join('');
return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>';
}
var SPLREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml };
if (typeof module !== 'undefined' && module.exports) module.exports = SPLREPORT;
else window.SPLREPORT = SPLREPORT;
</script>
<script>
(function(){
var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today');
today.value=new Date().toISOString().slice(0,10);
function run(){ try { var res=window.SPLENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.SPLREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } }
go.onclick=run; if (src.value.trim()) run();
})();
</script>
<section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://open-vsx.org/extension/readystack/spl-registration-lint" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'spl-registration-lint',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><a href="https://www.npmjs.com/package/@readystack/spl-registration-lint" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'spl-registration-lint',src:'npm',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://hub.docker.com/r/getreadystack/spl-registration-lint" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'spl-registration-lint',src:'docker',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">npx @readystack/spl-registration-lint <file>
docker run --rm -v "$PWD:/w" getreadystack/spl-registration-lint /w</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/cl/polar_cl_l6iN1uWt0FwWu7tBsczD0jWpP2vxFM54Wdwqb3KPi1G" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
<section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get the full rule table as a PDF</b><div style="opacity:.78;margin:.35rem 0 .8rem">All 9 rules SPL Registration Lint - FDA Establishment XML checks, each with its source and date, on printable pages you can hand to your team or auditor. We email it now, and once more only if a rule changes. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Email me the PDF</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">Sent. It arrives within the hour - check spam too.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"spl-registration-lint",lang:"en",source:"rule_table"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);})();</script></section>
<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Finds the lines the FDA gateway rejects in an SPL establishment registration, and how many days are left in the October 1 - December 31 window.</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">A regulatory-affairs consultant bills $100-$200 per hour.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_5DGxPviszTwjOqxMnoZEuBHdaWberjjix1mAZ1mQljU" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
<!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script>
<script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
<script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div><section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-21">Updated 2026-09-21</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-21","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku152375_result_card.jpg" alt="SPL Registration Lint - FDA Establishment XML" loading="lazy" style="max-width:100%;border-radius:6px"></p>
<p>A US drug establishment's SPL registration XML, dated 20251110, expired on 2025-12-31 - that is 264 days before we linted it on 2026-09-21 - and the same file carried 7 blocking errors on 6 lines. The file was well-formed XML. It simply would not have been accepted.</p>
<p>Drug establishment registration in the United States is renewed once a year, and the window is October 1 through December 31 (FD&C Act section 510(b)(2)). A registration that is not renewed inside that window lapses on December 31. The renewal is not a web form - it is a Structured Product Labeling file, HL7 v3 XML, sent through the FDA Electronic Submissions Gateway, which answers with an acknowledgement or a rejection only after you have submitted.</p>
<p>That is the gap this linter sits in: it reads the same file in your editor and runs 9 rules, naming the line each time.</p>
<p>Here is what came back:</p>
<pre><code>ERR L4 doc_type Document type is "34391-3", not 51725-0.
ERR L4 loinc_system codeSystem is "2.16.840.1.113883.6.69", not the LOINC OID
2.16.840.1.113883.6.1.
ERR L6 lapse_clock This registration is dated 20251110, so it expired on
2025-12-31, 264 days ago.
ERR L7 doc_uuid <setId> repeats the <id> UUID, so the FDA ESG reads this as a
new registration instead of a new version.
ERR L8 version_number versionNumber is "0", not a positive integer.
ERR L12 duns_format D-U-N-S number "12-345-6789" is not nine digits.
ERR L18 duns_format D-U-N-S number "98765432" is not nine digits.</code></pre>
<p>Most of those are mistakes a person cannot see by reading. A D-U-N-S number typed as 12-345-6789 reads perfectly to a human and fails to a parser that wants nine digits. A setId that repeats the document id looks tidy; to the gateway it means "a brand new registration", not "version 2 of the one you already have". The code 34391-3 is a real LOINC code - it is the code for a prescription drug label, not for an establishment registration, which is 51725-0.</p>
<p>The ninth rule is the one that is not in the file at all: the calendar. The linter takes the date you pass it and compares it to the October 1 - December 31 window. Give it the same file as of 2026-09-21 and it reports the registration expired 264 days ago. Re-date that file to 20260215, lint it as of 2026-10-05, and the count drops to a single finding: February is month 2, before the window opens. Fix the date to 20261001 and the file returns 0 findings. The number moves because the arithmetic is real, not because a string changed.</p>
<p>The engine is one file that runs unchanged in Node and in the browser:</p>
<pre><code>var RULES = (typeof module !== 'undefined')
? require('./rules.json')
: window.SPL_RULES;</code></pre>
<p>So the free browser page and the VS Code extension cannot drift apart - same engine, byte for byte, nothing uploaded.</p>
<p>Free, with no key: open any .xml file, run the check, and get every blocking error with its line number and the corrected line. That finishes one file. The full version is $29 once and moves on a different axis: it sweeps every .xml file in the submission folder in one pass and exports a dated readiness report you keep with the submission record. One licence key per person or CI seat.</p>
<p>For scale: a regulatory-affairs consultant bills $100-$200 per hour, and a rejected gateway submission costs a re-check plus a resubmission cycle inside a window that closes on December 31. This year that window opens in 10 days and closes 101 days from 2026-09-21.</p>
<p>The tool checks an SPL file's structure. It is not legal or regulatory advice.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku152375_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku152375_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does SPL Registration Lint actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads an FDA Structured Product Labeling establishment registration XML file in your editor and runs 9 rules against it: the LOINC document type 51725-0, the LOINC code system OID, UUID document id and setId, versionNumber, the eight-digit effectiveTime, the October 1 - December 31 registration window, the lapse clock, and whether every D-U-N-S number is exactly nine digits. Each finding names the line.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is this for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Regulatory affairs and regulatory operations staff at US drug establishments - manufacturers, repackagers, relabelers and contract sites - who build or hand-edit SPL establishment registration XML before sending it through the FDA Electronic Submissions Gateway. It also fits developers who generate SPL files from a product database and need a check in the repository.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why will a free chatbot or XML validator not do this?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A generic XML validator checks well-formedness, not that your document code is 51725-0 or that your D-U-N-S extension is nine digits rather than 12-345-6789. A chatbot does not know today's date against the October 1 - December 31 window. The FDA gateway does know, but it only tells you after you have already submitted.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence key?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free, with no key: lint the open SPL XML file against all 9 rules and get every blocking error with its line number. That finishes one file. The licence key adds a different axis - sweeping every .xml file in the submission folder in one pass, and exporting a dated readiness report you keep with the submission record.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost if a person did it?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A regulatory-affairs consultant bills $100-$200 per hour, and a rejected gateway submission costs a re-check plus a resubmission cycle inside a window that closes on December 31. The full version is $29 once, one licence key per person or CI seat,. The free tier alone finishes one file.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just ask ChatGPT or another AI chat?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. SPL Registration Lint - FDA Establishment XML checks the file you open against 9 rules from a rule set dated 2026-09-21, and points at the exact line with the fix. For a filing, an audit or a client you need that dated result on your own files.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does SPL Registration Lint actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads an FDA Structured Product Labeling establishment registration XML file in your editor and runs 9 rules against it: the LOINC document type 51725-0, the LOINC code system OID, UUID document id and setId, versionNumber, the eight-digit effectiveTime, the October 1 - December 31 registration window, the lapse clock, and whether every D-U-N-S number is exactly nine digits. Each finding names the line."}}, {"@type": "Question", "name": "Who is this for?", "acceptedAnswer": {"@type": "Answer", "text": "Regulatory affairs and regulatory operations staff at US drug establishments - manufacturers, repackagers, relabelers and contract sites - who build or hand-edit SPL establishment registration XML before sending it through the FDA Electronic Submissions Gateway. It also fits developers who generate SPL files from a product database and need a check in the repository."}}, {"@type": "Question", "name": "Why will a free chatbot or XML validator not do this?", "acceptedAnswer": {"@type": "Answer", "text": "A generic XML validator checks well-formedness, not that your document code is 51725-0 or that your D-U-N-S extension is nine digits rather than 12-345-6789. A chatbot does not know today's date against the October 1 - December 31 window. The FDA gateway does know, but it only tells you after you have already submitted."}}, {"@type": "Question", "name": "What is free and what needs a licence key?", "acceptedAnswer": {"@type": "Answer", "text": "Free, with no key: lint the open SPL XML file against all 9 rules and get every blocking error with its line number. That finishes one file. The licence key adds a different axis - sweeping every .xml file in the submission folder in one pass, and exporting a dated readiness report you keep with the submission record."}}, {"@type": "Question", "name": "What would this cost if a person did it?", "acceptedAnswer": {"@type": "Answer", "text": "A regulatory-affairs consultant bills $100-$200 per hour, and a rejected gateway submission costs a re-check plus a resubmission cycle inside a window that closes on December 31. The full version is $29 once, one licence key per person or CI seat,. The free tier alone finishes one file."}}, {"@type": "Question", "name": "Why not just ask ChatGPT or another AI chat?", "acceptedAnswer": {"@type": "Answer", "text": "A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. SPL Registration Lint - FDA Establishment XML checks the file you open against 9 rules from a rule set dated 2026-09-21, and points at the exact line with the fix. For a filing, an audit or a client you need that dated result on your own files."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:152375,slug:"spl-registration-lint",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/dotnet-eol-support-gate"><span class="yj-rel-t">.NET EOL & CRA Support Window Gate</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/dora-ict-contract-clause-lint"><span class="yj-rel-t">DORA Art. 30 ICT Contract Clause Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/pld-liability-window-lint"><span class="yj-rel-t">EU Liability Window Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/ingress-nginx-retirement-lint"><span class="yj-rel-t">Ingress-NGINX Retirement Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/maven-central-publish-gate"><span class="yj-rel-t">Maven Central Publish Gate</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/openapi-security-contract-lint"><span class="yj-rel-t">OpenAPI Security Contract Lint</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section></body>
</html>
|