import { Router } from "express"; import { pool } from "../db.js"; const router = Router(); interface CoresData { frn: string; entity_name: string | null; address: string | null; city: string | null; state: string | null; zip: string | null; status: string | null; red_light: boolean | null; error: string | null; } interface RmdData { found: boolean; business_name: string | null; frn: string | null; rmd_number: string | null; certification_date: string | null; implementation_type: string | null; contact_name: string | null; contact_email: string | null; removed: boolean; removal_reason: string | null; error: string | null; } interface ComplianceCheck { id: string; label: string; status: "green" | "yellow" | "red" | "unknown"; detail: string; action_url: string | null; due_date: string | null; } /** * FCC Compliance Lookup * * GET /api/v1/fcc/lookup?frn=0012345678 * * Queries multiple public FCC data sources + our local DB to produce * a unified compliance status report for a given FRN. */ router.get("/api/v1/fcc/lookup", async (req, res) => { const frn = (req.query.frn as string || "").replace(/\D/g, "").padStart(10, "0"); const quickMode = req.query.quick === "1"; // Skip slow checks (corp status, RMD audit) if (!frn || frn.length !== 10 || frn === "0000000000") { res.status(400).json({ error: "Please provide a valid 10-digit FRN." }); return; } const startMs = Date.now(); try { // Phase 1: Fast local DB lookups first (~10ms) const [localRmdP, localRemovedP, local499P] = await Promise.allSettled([ fetchLocalRmd(frn), fetchLocalRemoved(frn), fetchLocal499Filer(frn), ]); const localRmdResult = localRmdP.status === "fulfilled" ? localRmdP.value : null; const local499Result = local499P.status === "fulfilled" ? local499P.value : null; const filerId = local499Result?.filer_id || null; // Phase 2: External API calls — only fetch what we DON'T have locally. // For carriers in our DB (most of them), skip the slow CORES + RMD scrapes entirely. const hasLocalData = !!(localRmdResult || local499Result); const phase2: Record> = {}; if (!hasLocalData) { // Unknown carrier — need live CORES + RMD lookups phase2.cores = fetchCoresData(frn); phase2.rmd = fetchRmdData(frn); } else { // Known carrier — use local data, skip slow external scrapes phase2.cores = Promise.resolve(null); phase2.rmd = Promise.resolve(null); } if (filerId) { phase2.filerDetail = fetch499Detail(filerId); phase2.cpniResult = fetchCpniStatus(filerId); } // Corp status const corpEntityName = localRmdResult?.business_name || local499Result?.legal_name; const corpPhysicalState = local499Result?.state; if (!quickMode && corpEntityName && corpPhysicalState && !(localRmdResult?.foreign_voice_provider === true)) { phase2.corpStatus = (async () => { try { const { lookupCorpStatus } = await import("./corp-status.js"); // Check physical state for foreign entity redirect const localMatch = await pool.query( `SELECT entity_type, formation_state FROM entity_cache WHERE state = $1 AND LOWER(entity_name) = LOWER($2) LIMIT 1`, [corpPhysicalState, corpEntityName], ).catch(() => ({ rows: [] })); if (localMatch.rows.length > 0) { const m = localMatch.rows[0] as Record; const eType = ((m.entity_type as string) || "").toUpperCase(); const formSt = (m.formation_state as string) || ""; if (eType.includes("FOREIGN") && formSt && formSt !== corpPhysicalState) { return lookupCorpStatus(corpEntityName, formSt); } } return lookupCorpStatus(corpEntityName, corpPhysicalState!); } catch { return null; } })(); } // Wait for all phase 2 in parallel const phase2Keys = Object.keys(phase2); const phase2Results = await Promise.allSettled(Object.values(phase2)); const p2: Record = {}; phase2Keys.forEach((k, i) => { p2[k] = phase2Results[i].status === "fulfilled" ? (phase2Results[i] as PromiseFulfilledResult).value : null; }); let filerDetail = (p2.filerDetail as { current_as_of: string | null; comm_type: string | null; contributor: boolean | null; hq_address: string | null; hq_city: string | null; hq_state: string | null; hq_zip: string | null; error: string | null }) || null; let cpniResult = (p2.cpniResult as { filed: boolean; cert_year: number | null; date_filed: string | null; error: string | null }) || null; const coresResult = (p2.cores as CoresData) || { frn, entity_name: null, address: null, city: null, state: null, zip: null, status: null, red_light: null, error: "CORES lookup failed" } as CoresData; const rmdResult = (p2.rmd as RmdData) || { found: false, business_name: null, frn: null, rmd_number: null, certification_date: null, implementation_type: null, contact_name: null, contact_email: null, removed: false, removal_reason: null, error: "RMD lookup failed" } as RmdData; const localRemovedResult = localRemovedP.status === "fulfilled" ? localRemovedP.value : null; // Prefer local 499 filer data over CORES scrape for entity info if (!coresResult.entity_name && local499Result) { coresResult.entity_name = local499Result.legal_name; coresResult.state = local499Result.state; coresResult.status = "found_local"; coresResult.error = null; } // Merge RMD data: prefer live API, enrich from local DB if (localRmdResult) { if (!rmdResult.found) { rmdResult.found = true; rmdResult.business_name = localRmdResult.business_name; rmdResult.rmd_number = localRmdResult.rmd_number; rmdResult.contact_name = localRmdResult.contact_name || localRmdResult.robocall_contact_name; rmdResult.contact_email = localRmdResult.contact_email || localRmdResult.robocall_contact_email; rmdResult.implementation_type = localRmdResult.implementation; } // Use best available date: last_recertified > last_updated > csv_imported_at // Normalize all dates to YYYY-MM-DD strings if (!rmdResult.certification_date) { const rawDate = localRmdResult.last_recertified || localRmdResult.last_updated || localRmdResult.csv_imported_at; if (rawDate) { const d = rawDate instanceof Date ? rawDate : new Date(rawDate); rmdResult.certification_date = d.toISOString().slice(0, 10); } } // Check if the provider has been removed from RMD if (localRmdResult.removed_from_rmd) { rmdResult.removed = true; rmdResult.removal_reason = `Removed from RMD registry on ${localRmdResult.removed_at ? new Date(localRmdResult.removed_at).toISOString().slice(0, 10) : "unknown date"}. Provider was previously registered but no longer appears in the FCC Robocall Mitigation Database.`; } } // Build compliance checks const checks: ComplianceCheck[] = []; // 1. FCC CORES Registration — prefer local data (RMD/499 filer), fall back to live scrape const localEntityName = localRmdResult?.business_name || local499Result?.legal_name; const coresName = coresResult.entity_name || localEntityName; const coresSource = coresResult.entity_name ? "CORES" : (localRmdResult?.business_name ? "RMD registry" : (local499Result?.legal_name ? "499 filer database" : null)); // Red light: check local DB first (populated by CORES Playwright scraper), then CORES scrape const localRedLight = localRmdResult?.red_light_status; const isRedLight = localRedLight === "red" || coresResult.red_light === true; const isGreenLight = localRedLight === "green" || coresResult.red_light === false; const redLightKnown = localRedLight != null || coresResult.red_light != null; let coresStatus: "green" | "yellow" | "red" | "unknown" = "unknown"; let coresDetail = ""; if (coresName) { if (isRedLight) { coresStatus = "red"; coresDetail = `${coresName} — RED LIGHT (outstanding delinquent debts)`; } else if (isGreenLight) { coresStatus = "green"; coresDetail = `${coresName} — GREEN (no delinquent debts)`; } else { coresStatus = "green"; coresDetail = `${coresName} — Active`; } if (coresSource && coresSource !== "CORES") coresDetail += ` (via ${coresSource})`; } else { coresDetail = "FRN not found in local registry. Verify directly at FCC CORES."; } checks.push({ id: "cores_registration", label: "FCC CORES Registration", status: coresStatus, detail: coresDetail, action_url: null, due_date: null, }); // 2. RMD Filing const rmdRemoved = localRemovedResult !== null; let rmdStatus: "green" | "yellow" | "red" | "unknown" = "unknown"; let rmdDetail = ""; if (rmdRemoved) { rmdStatus = "red"; rmdDetail = `REMOVED from RMD — ${localRemovedResult.removal_reason || "See FCC order"}. Immediate action required.`; } else if (rmdResult.removed) { rmdStatus = "red"; rmdDetail = rmdResult.removal_reason || "Removed from Robocall Mitigation Database. Immediate action required."; } else if (rmdResult.found) { const implLabel = rmdResult.implementation_type || "compliant"; if (rmdResult.certification_date) { const certDate = new Date(rmdResult.certification_date); const monthsSince = (Date.now() - certDate.getTime()) / (1000 * 60 * 60 * 24 * 30); if (monthsSince > 13) { rmdStatus = "red"; rmdDetail = `Certification expired — last updated ${rmdResult.certification_date}. Annual recertification required by March 1.`; } else if (monthsSince > 10) { rmdStatus = "yellow"; rmdDetail = `Certification expiring soon — last updated ${rmdResult.certification_date}. Recertification due March 1.`; } else { rmdStatus = "green"; rmdDetail = `${implLabel} — last updated ${rmdResult.certification_date}`; } } else { // Present in RMD CSV but no date available — still means they're registered rmdStatus = "green"; rmdDetail = `${implLabel} — registered in RMD`; if (rmdResult.rmd_number) rmdDetail += ` (${rmdResult.rmd_number})`; } } else { rmdStatus = "red"; rmdDetail = "Not found in Robocall Mitigation Database. Registration is required for all voice service providers. If you are a data-only provider, RMD does not apply."; } checks.push({ id: "rmd_filing", label: "Robocall Mitigation Database (RMD)", status: rmdStatus, detail: rmdDetail, action_url: null, due_date: "March 1 (annual recertification)", }); // 2b. RMD Filing Quality — real-time structured analysis of the filing // Checks for 2026 compliance: STIR/SHAKEN consistency, provider classification, // missing contact info, and logical combinations. if (rmdResult.found && !rmdRemoved && !quickMode && localRmdResult) { try { const rmdIssues: Array<{ label: string; severity: string }> = []; // Check: Provider classification — at least one must be selected const isVSP = localRmdResult.voice_service_provider === true; const isGW = localRmdResult.gateway_provider === true; const isInter = localRmdResult.intermediate_provider === true; if (!isVSP && !isGW && !isInter) { rmdIssues.push({ label: "No provider classification selected", severity: "critical" }); } // Check: STIR/SHAKEN consistency const impl = (localRmdResult.implementation || "").toLowerCase(); if (isVSP && !isGW && !isInter) { if (impl.includes("robocall mitigation") && !impl.includes("partial") && !impl.includes("complete")) { rmdIssues.push({ label: "VSP without STIR/SHAKEN (robocall mitigation only)", severity: "major" }); } } if (isInter && !isVSP && !isGW) { if (impl.includes("complete") && !impl.includes("partial")) { rmdIssues.push({ label: "Intermediate provider claims Complete STIR/SHAKEN", severity: "major" }); } } // Note: contact_email/name are often NULL in our local DB because the RMD CSV // doesn't include them (requires separate scrape). Not a filing deficiency. // Check: 2026 requirements — if we have the PDF audit cached, include those try { const auditRow = await pool.query( `SELECT total_deficiencies, severity, structured_checks, pdf_checks FROM fcc_rmd_audit_results WHERE frn = $1 AND audited_at > NOW() - INTERVAL '30 days' LIMIT 1`, [frn], ); if (auditRow.rows.length > 0) { const audit = auditRow.rows[0] as Record; // Merge both structured and PDF audit checks const pChecks = (audit.pdf_checks as Array<{ id?: string; label: string; severity: string }>) || []; const sChecks = (audit.structured_checks as Array<{ id?: string; label: string; severity: string }>) || []; for (const pc of [...sChecks, ...pChecks]) { if (pc.severity === "minor") continue; // only show major/critical if (pc.label && !rmdIssues.some(i => i.label === pc.label)) { rmdIssues.push(pc); } } } } catch { /* audit table may not exist */ } if (rmdIssues.length > 0) { const worstSev = rmdIssues.some(i => i.severity === "critical") ? "critical" : rmdIssues.some(i => i.severity === "major") ? "major" : "minor"; const labels = rmdIssues.map(i => i.label).slice(0, 5); checks.push({ id: "rmd_quality", label: "RMD Filing Quality (2026)", status: worstSev === "critical" ? "red" : "yellow", detail: `${rmdIssues.length} issue(s) found: ${labels.join("; ")}${labels.length < rmdIssues.length ? ` (+${rmdIssues.length - labels.length} more)` : ""}. Your filing does not meet all 2026 FCC requirements. We recommend refiling your RMD certification with the missing sections included.`, action_url: null, due_date: null, }); } } catch { // Silently skip on error } } // 3. STIR/SHAKEN // // Data source: FCC Robocall Mitigation Database (RMD) — this is the // carrier's SELF-REPORTED implementation status. It is NOT verified // against the STI-PA (iConectiv) certificate authority database. // A carrier can claim "Complete STIR/SHAKEN" in RMD without actually // holding an active STI certificate from iConectiv. // // The authoritative source for certificate verification is the STI-PA // at authenticate.iconectiv.com, which tracks actual certificate // issuance by OCN. We flag the self-reported status and recommend // the carrier confirm their cert is active with their STI-CA. let ssStatus: "green" | "yellow" | "red" | "unknown" = "unknown"; let ssDetail = ""; if (rmdResult.found && rmdResult.implementation_type) { const impl = rmdResult.implementation_type.toLowerCase(); const certDate = rmdResult.certification_date || ""; if (impl.includes("complete") || impl.includes("full")) { ssStatus = "green"; ssDetail = `Full STIR/SHAKEN implementation self-reported in RMD` + (certDate ? ` (certified ${certDate})` : "") + `. Note: this is the carrier's filing with the FCC, not a ` + `verification from the STI-PA (iConectiv). Confirm your STI ` + `certificate is active with your Certificate Authority and ` + `that your SPC token has not expired.`; } else if (impl.includes("partial") || impl.includes("robocall mitigation")) { ssStatus = "yellow"; ssDetail = `Partial implementation — robocall mitigation plan filed ` + `but full STIR/SHAKEN not certified in RMD` + (certDate ? ` (last updated ${certDate})` : "") + `. Full implementation requires an STI certificate from an ` + `approved CA (e.g. Sievert Larsen, Neustar, TransNexus) and ` + `registration with the STI-PA at authenticate.iconectiv.com.`; } else { ssStatus = "yellow"; ssDetail = `Implementation type: ${rmdResult.implementation_type}. Verify certificate status with your STI-CA.`; } } else if (rmdResult.found) { ssStatus = "red"; ssDetail = `RMD filing exists but no STIR/SHAKEN implementation type ` + `reported. If you are a voice service provider, you must ` + `either implement STIR/SHAKEN (obtain an STI certificate ` + `from an approved CA) or file a robocall mitigation plan.`; } else { ssDetail = "Cannot determine — no RMD filing found for this FRN. Verify STIR/SHAKEN status directly."; } // STIR/SHAKEN — computed but NOT shown on the compliance checker. // The self-reported RMD status doesn't reliably indicate compliance, // and showing it creates confusion with OCN bundling on the order page. // Kept computed for internal use / future STI-PA verification. // checks.push({ // id: "stir_shaken", // label: "STIR/SHAKEN Call Authentication", // status: ssStatus, // detail: ssDetail, // action_url: null, // due_date: null, // }); // 4. CPNI Annual Certification // // CPNI rule: by March 1 each year, every carrier must certify its // compliance for the *prior* calendar year (47 CFR § 64.2009(e)). // So on any date in year Y: // - The "required cert year" is Y-1 (e.g. in 2026 you're certifying // your 2025 compliance). // - That cert's deadline is March 1 of year Y. // - After March 1 of year Y with no filing → PAST DUE for Y-1; // the missed deadline is the one we surface (NOT bumped a year // forward — that would make a late filer look on-time). // - Once the required cert is on file, the NEXT deadline is // March 1 of year Y+1 for certifying year Y revenue. const now = new Date(); const has499Filer = local499Result !== null; const isForeignProvider = localRmdResult?.foreign_voice_provider === true; // Required cert year = prior calendar year. const requiredCertYear = now.getFullYear() - 1; // Deadline for the required cert = March 1 of the current year. const requiredCertDeadline = new Date(now.getFullYear(), 2, 1); // Next annual deadline (only meaningful once the required cert is filed). const nextAnnualDeadline = new Date(now.getFullYear() + 1, 2, 1); const daysTo = (d: Date) => Math.ceil((d.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)); const requiredDeadlineIso = requiredCertDeadline.toISOString().slice(0, 10); const nextDeadlineIso = nextAnnualDeadline.toISOString().slice(0, 10); let cpniStatus: "green" | "yellow" | "red" | "unknown" = "unknown"; let cpniDetail = ""; let cpniDueDate = requiredDeadlineIso; if (has499Filer && cpniResult?.filed) { if (cpniResult.cert_year! >= requiredCertYear) { // They've certified for the required year or later. Next deadline // is the following March 1. cpniStatus = "green"; cpniDueDate = nextDeadlineIso; cpniDetail = `Filer ID: ${filerId}. Filed for ${cpniResult.cert_year} ` + `on ${cpniResult.date_filed}. Next due ${nextDeadlineIso}.`; } else { // Older cert on record — they're past due for the most recent year. cpniStatus = "red"; cpniDueDate = requiredDeadlineIso; cpniDetail = `Filer ID: ${filerId}. Past due — last filed for ` + `${cpniResult.cert_year} (${cpniResult.date_filed}). ` + `${requiredCertYear} certification was required by ${requiredDeadlineIso}.`; } } else if (has499Filer) { // No filing on record. If the required deadline is behind us, it's // past due; otherwise it's upcoming. if (requiredCertDeadline < now) { const daysOverdue = -daysTo(requiredCertDeadline); cpniStatus = "red"; cpniDueDate = requiredDeadlineIso; cpniDetail = `Filer ID: ${filerId}. PAST DUE — ${requiredCertYear} ` + `certification was required by ${requiredDeadlineIso} ` + `(${daysOverdue} days overdue). File immediately to reduce ` + `47 CFR § 1.80 forfeiture exposure.`; } else { const daysUntil = daysTo(requiredCertDeadline); if (daysUntil <= 30) { cpniStatus = "yellow"; cpniDetail = `Filer ID: ${filerId}. Due in ${daysUntil} days ` + `(${requiredDeadlineIso}). Certifying ${requiredCertYear} compliance.`; } else { cpniStatus = "red"; cpniDetail = `Filer ID: ${filerId}. No CPNI filing found. ` + `${requiredCertYear} certification required by ${requiredDeadlineIso}.`; } } } else { if (isForeignProvider) { cpniStatus = "green"; cpniDetail = "Not required — foreign voice providers operating outside the US are generally exempt from CPNI certification requirements."; } else { cpniDetail = `No 499 Filer ID found. If you are a US-based voice carrier, you may need to register with USAC and file CPNI certification by ${requiredDeadlineIso}. Foreign providers are typically exempt.`; } } checks.push({ id: "cpni_certification", label: "CPNI Annual Certification", status: cpniStatus, detail: cpniDetail, action_url: null, due_date: cpniDueDate, }); // 5. Form 499-A // // Annual 499-A is due April 1 each year, reporting the PRIOR calendar // year's revenue (47 CFR § 54.711). Same past-due pattern as CPNI: // surface the MISSED deadline when no registration is on file past // April 1, not a year-bumped future date. const requiredFilingYear = now.getFullYear() - 1; // reporting year const requiredFilingDue = new Date(now.getFullYear(), 3, 1); // Apr 1 const nextAnnualFilingDue = new Date(now.getFullYear() + 1, 3, 1); const requiredFilingIso = requiredFilingDue.toISOString().slice(0, 10); const nextFilingIso = nextAnnualFilingDue.toISOString().slice(0, 10); let f499Status: "green" | "yellow" | "red" | "unknown" = "unknown"; let f499Detail = ""; let f499DueDate = requiredFilingIso; const currentAsOf = filerDetail?.current_as_of || null; if (has499Filer) { if (currentAsOf) { // Parse "Registration Current as of" (M/D/YYYY). If the date is // on/after this year's April 1 (or after the reporting year's // April 1 before we've hit April), the filing is current. const regDate = new Date(currentAsOf); if (regDate >= requiredFilingDue) { f499Status = "green"; f499DueDate = nextFilingIso; f499Detail = `Filer ID: ${filerId}. Registration current as of ` + `${currentAsOf}. Next due ${nextFilingIso}.`; } else { // Registration date is before the current filing deadline. // They filed for a prior year but haven't filed the current one. // Compute which year the last filing likely covered: // a filing on 4/1/2025 covers CY2024 revenue. const lastFiledYear = regDate.getFullYear() - 1; const daysOverdue = requiredFilingDue < now ? Math.ceil((now.getTime() - requiredFilingDue.getTime()) / (1000 * 60 * 60 * 24)) : 0; f499Status = requiredFilingDue < now ? "red" : "yellow"; f499DueDate = requiredFilingIso; f499Detail = requiredFilingDue < now ? `Filer ID: ${filerId}. PAST DUE — last filing covered CY${lastFiledYear} ` + `(filed ${currentAsOf}). The CY${requiredFilingYear} annual 499-A was due ` + `${requiredFilingIso} (${daysOverdue} days overdue).` : `Filer ID: ${filerId}. Last filing covered CY${lastFiledYear} ` + `(filed ${currentAsOf}). CY${requiredFilingYear} 499-A due ${requiredFilingIso}.`; } } else if (requiredFilingDue < now) { // No "current as of" date + deadline passed → past due. const daysOverdue = Math.ceil( (now.getTime() - requiredFilingDue.getTime()) / (1000 * 60 * 60 * 24), ); f499Status = "red"; f499DueDate = requiredFilingIso; f499Detail = `Filer ID: ${filerId}. PAST DUE — ${requiredFilingYear} annual ` + `499-A required by ${requiredFilingIso} (${daysOverdue} days overdue). ` + `Could not verify filing status; file immediately.`; } else { const daysUntil = Math.ceil( (requiredFilingDue.getTime() - now.getTime()) / (1000 * 60 * 60 * 24), ); if (daysUntil <= 30) { f499Status = "yellow"; f499DueDate = requiredFilingIso; f499Detail = `Due in ${daysUntil} days (${requiredFilingIso}). Filer ID: ${filerId}.`; } else { f499Status = "unknown"; f499DueDate = requiredFilingIso; f499Detail = `Filer ID: ${filerId}. Could not verify filing status. ` + `Next due ${requiredFilingIso}.`; } } } else { if (isForeignProvider) { f499Status = "green"; f499Detail = "Not required — foreign voice providers are generally exempt from FCC Form 499 filing and USF contribution obligations."; } else { f499Detail = `No 499 Filer ID found for this FRN. If you are a US-based carrier, you must register with USAC and file Form 499-A annually (next due ${requiredFilingIso}). Foreign providers are typically exempt.`; } } checks.push({ id: "form_499a", label: "FCC Form 499-A (Annual)", status: f499Status, detail: f499Detail, action_url: null, due_date: f499DueDate, }); // 6. Form 499-Q (quarterly) // // Quarterly projections are due Feb 1 / May 1 / Aug 1 / Nov 1 each // year. Required for USF contributors (waived for de-minimis filers). // We don't track per-quarter filings in our DB, so we surface BOTH the // most recently passed deadline (so the customer can self-check past // due) and the next upcoming deadline. const quarterDues = [ new Date(now.getFullYear() - 1, 10, 1), // prior Nov 1 (for Jan–Feb window) new Date(now.getFullYear(), 1, 1), // Feb 1 new Date(now.getFullYear(), 4, 1), // May 1 new Date(now.getFullYear(), 7, 1), // Aug 1 new Date(now.getFullYear(), 10, 1), // Nov 1 new Date(now.getFullYear() + 1, 1, 1), // next-year Feb 1 ]; const next499Q = quarterDues.find(d => d > now) ?? new Date(now.getFullYear() + 1, 1, 1); const last499Q = [...quarterDues].reverse().find(d => d <= now); const days499QUntil = Math.ceil( (next499Q.getTime() - now.getTime()) / (1000 * 60 * 60 * 24), ); const next499QIso = next499Q.toISOString().slice(0, 10); const last499QIso = last499Q ? last499Q.toISOString().slice(0, 10) : null; const isContributor = filerDetail?.contributor; let q499Status: "green" | "yellow" | "red" | "unknown" = "unknown"; let q499Detail = ""; let q499DueDate = next499QIso; // De minimis explainer — appended to 499-Q detail when relevant. // The nuance most carriers miss: de minimis filers are EXEMPT from // collecting and remitting USF to USAC, but they still OWE USF to // their upstream vendors (the vendor charges it on their bill and // remits it to USAC themselves). So de minimis doesn't mean "no USF // cost" — it means you pay USF embedded in your vendor invoices // instead of collecting a line-item surcharge from your own customers. const DE_MINIMIS_NOTE = `De minimis filers (estimated annual USF contribution < $10,000) ` + `are exempt from filing 499-Q and from collecting USF surcharges ` + `from end users. However, de minimis does NOT eliminate your USF ` + `cost — your upstream carriers (IXCs, CLECs, VoIP providers) still ` + `charge you USF on their invoices and remit it to USAC on your ` + `behalf. The trade-off: filing as a full contributor lets you ` + `pass the USF charge through to your customers as a line-item ` + `surcharge (revenue-neutral); filing de minimis means you absorb ` + `the cost yourself.`; if (isForeignProvider && !has499Filer) { q499Status = "green"; q499Detail = "Not required — foreign voice providers are generally exempt from USF quarterly projections."; } else if (!has499Filer) { // No 499 Filer ID — can't have a quarterly due if not registered with USAC q499Status = "unknown"; q499Detail = "No 499 Filer ID found. 499-Q is only required for registered USF contributors. If you file 499-A as de minimis, 499-Q is waived."; } else if (isContributor === false) { // USAC records show this filer as a non-contributor. q499Status = "green"; q499Detail = `Not currently a USF contributor — 499-Q not required. ` + `Next deadline ${next499QIso} if contribution status changes. ` + DE_MINIMIS_NOTE; } else if (f499Status === "red") { // Annual 499-A is past due; quarterly is almost certainly overdue too. q499Status = "red"; q499DueDate = last499QIso ?? next499QIso; q499Detail = `PAST DUE — 499-A is delinquent, so quarterly projections ` + `(last deadline ${last499QIso}) are almost certainly overdue. ` + `Next deadline ${next499QIso}.`; } else if (days499QUntil <= 30) { q499Status = "yellow"; q499Detail = `Due in ${days499QUntil} days (${next499QIso}). ` + `Not required if you elected de minimis on your 499-A. ` + DE_MINIMIS_NOTE + (last499QIso ? ` Confirm the ${last499QIso} quarterly was filed on time.` : ""); } else { q499Status = "unknown"; q499Detail = `Next due ${next499QIso}. ` + `Required for USF contributors; waived for de minimis filers. ` + DE_MINIMIS_NOTE + (last499QIso ? ` Most recent deadline was ${last499QIso} — if you're a ` + `contributor, confirm that quarterly was filed.` : ""); } checks.push({ id: "form_499q", label: "FCC Form 499-Q (Quarterly)", status: q499Status, detail: q499Detail, action_url: null, due_date: q499DueDate, }); // 7. Broadband Data Collection (BDC / Form 477) // Auto-flag likely broadband providers by comm type; show unknown for VoIP-only const commType = (filerDetail?.comm_type || "").toLowerCase(); const likelyBroadband = [ "cellular", "pcs", "smr", "cable", "local exchange", "ilec", "clec", "broadband", "isp", "internet", "fiber", "fixed wireless", "satellite", ].some(t => commType.includes(t)); let bdcStatus: "green" | "yellow" | "red" | "unknown" = "unknown"; let bdcDetail = ""; if (isForeignProvider && !has499Filer) { bdcStatus = "green"; bdcDetail = "Not required — BDC filing applies only to providers offering broadband or voice service within the United States."; } else if (likelyBroadband) { bdcStatus = "yellow"; bdcDetail = `Comm type "${filerDetail?.comm_type}" suggests broadband service. BDC filing likely required — confirm below.`; } else if (has499Filer) { bdcDetail = `Comm type "${filerDetail?.comm_type || "unknown"}". BDC filing is required if you provide broadband internet access OR retail voice service to end users in the United States.`; } else { bdcDetail = "BDC filing is required if you provide broadband internet access OR retail voice service to end users in the United States."; } checks.push({ id: "bdc_filing", label: "BDC Filing (Broadband + Voice Subscription Data)", status: bdcStatus, detail: bdcDetail, action_url: null, due_date: null, }); // 8. Foreign provider recommendation (skip for Canadian carriers — they're already in North America) const providerCountry = (localRmdResult?.country || "").trim().toLowerCase(); if (isForeignProvider && providerCountry !== "canada") { const country = localRmdResult?.country || "your country"; checks.push({ id: "foreign_carrier_recommendation", label: "Expand Into North America", status: "yellow", detail: `Your carrier is registered as a foreign voice provider (${country}). ` + `Establishing a North American carrier entity can give you direct vendor relationships, ` + `simplified billing with US/Canadian partners, and local number access. ` + `We can help you launch a Canadian CRTC-licensed carrier (BC corporation + full CRTC registration) ` + `or a US FCC-authorized carrier (state formation + CORES/FRN + RMD + 499-A + CPNI — everything included).`, action_url: null, due_date: null, }); } // 9. Corporation Good Standing (result already fetched in parallel phase 2) const corpResult = p2.corpStatus as { found: boolean; entity_name: string; entity_type: string; status: string; state: string; years_behind: number; breakdown: string; principal_address: string } | null; if (corpResult?.found) { const cStatus = (corpResult.status || "").toUpperCase(); const corpSt = corpResult.state || ""; let corpCheckStatus: "green" | "yellow" | "red" | "unknown" = "unknown"; let corpDetail = ""; const stateAddr = corpResult.principal_address || ""; const coresAddr = coresResult.address || ""; let addrNote = ""; if (stateAddr && coresAddr) { const normState = stateAddr.toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 30); const normCores = coresAddr.toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 30); if (normState && normCores && normState !== normCores) { addrNote = ` Note: state filing address (${stateAddr}) differs from FCC CORES address (${coresAddr}) — verify which is current.`; } } else if (stateAddr) { addrNote = ` Address on file: ${stateAddr}.`; } if (cStatus === "ACTIVE") { corpCheckStatus = "green"; corpDetail = `${corpResult.entity_name} (${corpResult.entity_type || "entity"}) in ${corpSt} — Active / Good Standing.${addrNote}`; } else if (cStatus === "DELINQUENT" || cStatus === "SUSPENDED") { corpCheckStatus = "red"; corpDetail = `${corpResult.entity_name} in ${corpSt} shows status: ${cStatus}. ` + (corpResult.years_behind > 0 ? `You are ${corpResult.years_behind} year${corpResult.years_behind > 1 ? "s" : ""} behind on annual reports. ${corpResult.breakdown}` : `Annual report may be overdue. Contact us for a status review.`) + addrNote; } else if (cStatus === "DISSOLVED" || cStatus === "INACTIVE") { corpCheckStatus = "red"; corpDetail = `${corpResult.entity_name} in ${corpSt} shows status: ${cStatus}. ` + `Reinstatement required to restore good standing. ` + (corpResult.breakdown || "Contact us for a reinstatement quote.") + addrNote; } else { corpDetail = `${corpResult.entity_name} in ${corpSt} — status: ${cStatus}.${addrNote}`; } checks.push({ id: "corporation_status", label: "Corporation Good Standing", status: corpCheckStatus, detail: corpDetail, action_url: null, due_date: null, }); } // Entity info (best available name — local sources preferred over CORES scrape) const entityName = local499Result?.legal_name || coresResult.entity_name || rmdResult.business_name || localRmdResult?.business_name || null; res.json({ frn, entity_name: entityName, cores: { entity_name: coresResult.entity_name, address: coresResult.address || filerDetail?.hq_address || null, city: coresResult.city || filerDetail?.hq_city || null, state: coresResult.state || filerDetail?.hq_state || null, zip: coresResult.zip || filerDetail?.hq_zip || null, red_light: coresResult.red_light, error: coresResult.error, }, rmd: { found: rmdResult.found, removed: rmdRemoved, removal_reason: localRemovedResult?.removal_reason || null, rmd_number: rmdResult.rmd_number || localRmdResult?.rmd_number || null, certification_date: rmdResult.certification_date || localRmdResult?.cert_date || null, implementation_type: rmdResult.implementation_type || localRmdResult?.implementation_type || null, business_address: localRmdResult?.business_address || null, contact_name: rmdResult.contact_name || localRmdResult?.robocall_contact_name || null, other_dba_names: localRmdResult?.other_dba_names || null, previous_dba_names: localRmdResult?.previous_dba_names || null, other_frns: localRmdResult?.other_frns || null, foreign_voice_provider: localRmdResult?.foreign_voice_provider ?? null, provider_types: [ localRmdResult?.voice_service_provider ? "Voice Service Provider" : null, localRmdResult?.gateway_provider ? "Gateway Provider" : null, localRmdResult?.intermediate_provider ? "Intermediate Provider" : null, ].filter(Boolean), error: rmdResult.error, }, filer_499: local499Result ? { filer_id: local499Result.filer_id, legal_name: local499Result.legal_name, trade_name: local499Result.trade_name, state: local499Result.state, service_type: local499Result.service_type, current_as_of: filerDetail?.current_as_of || null, comm_type: filerDetail?.comm_type || null, usf_contributor: filerDetail?.contributor ?? null, cpni_cert_year: cpniResult?.cert_year || null, cpni_date_filed: cpniResult?.date_filed || null, } : null, checks, checked_at: new Date().toISOString(), }); // Log the check for analytics (non-blocking) try { const issueCount = checks?.filter((c: any) => c.status === "red" || c.status === "yellow").length || 0; const worstSeverity = checks?.some((c: any) => c.status === "red") ? "critical" : checks?.some((c: any) => c.status === "yellow") ? "major" : checks?.some((c: any) => c.status === "green") ? "clean" : "clean"; const flaggedSlugs = checks ?.filter((c: any) => c.status === "red" || c.status === "yellow") .map((c: any) => c.id || c.slug || "") .filter(Boolean) || []; const elapsed = Date.now() - startMs; pool.query( `INSERT INTO compliance_check_log (frn, entity_name, ip_address, user_agent, referrer, total_checks, issues_found, severity, check_slugs, response_ms) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [ frn, entityName || null, (req as any).clientIp || req.ip || null, req.headers["user-agent"]?.slice(0, 200) || null, req.headers.referer?.slice(0, 200) || null, checks?.length || 0, issueCount, worstSeverity, flaggedSlugs.length > 0 ? flaggedSlugs : null, elapsed, ], ).catch(() => {}); // non-blocking, don't fail the response } catch { /* ignore logging errors */ } } catch (err) { console.error("[fcc-lookup] Error:", err); res.status(500).json({ error: "FCC lookup failed. Please try again." }); } }); // ── Data fetchers ──────────────────────────────────────────────────────── async function fetchCoresData(frn: string): Promise { // FCC CORES public search detail page — returns entity info without login const url = `https://apps.fcc.gov/cores/searchDetail.do?frn=${frn}`; try { const resp = await fetch(url, { signal: AbortSignal.timeout(10000), headers: { "Accept": "text/html,application/xhtml+xml", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", }, redirect: "follow", }); if (!resp.ok) { return { frn, entity_name: null, address: null, city: null, state: null, zip: null, status: null, red_light: null, error: `CORES returned ${resp.status}` }; } const html = await resp.text(); // Parse th/td pairs from the CORES detail page. // Format: Entity Name: \n Falcon Broadband, LLC const fields: Record = {}; const pairRegex = /]*>([^<]+)<\/th>\s*]*>([\s\S]*?)<\/td>/gi; let match; while ((match = pairRegex.exec(html)) !== null) { const key = match[1].replace(/[:\s]+$/g, "").trim().toLowerCase(); const val = match[2].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim(); if (val && val.length > 1 && val.length < 200) { fields[key] = val; } } const entityName = fields["entity name"] || fields["registration name"] || null; // Parse address from "Contact Address" field. // Raw HTML has
separating lines: "12920 SE 38th Street
Bellevue, WA 98006
United States" // We need to split on
BEFORE stripping tags to preserve line structure. let address: string | null = null; let city: string | null = null; let state: string | null = null; let zip: string | null = null; // Re-extract the raw contact address (with
tags intact) from HTML const addrMatch = html.match(/]*>Contact Address:<\/th>\s*]*>([\s\S]*?)<\/td>/i); if (addrMatch) { // Split on
tags to get individual lines const lines = addrMatch[1] .split(//i) .map(l => l.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim()) .filter(l => l.length > 0 && l !== "United States" && l !== "US"); if (lines.length >= 2) { // First line(s) = street, last line with city/state/zip pattern for (let i = lines.length - 1; i >= 0; i--) { const csz = lines[i].match(/^(.+),\s*([A-Z]{2})\s+(\d{5}(?:-\d{4})?)$/); if (csz) { city = csz[1].trim(); state = csz[2]; zip = csz[3]; // Everything before this line is the street address address = lines.slice(0, i).join(", ") || null; break; } } // Fallback: if no city/state/zip pattern found, use first line as address if (!address && lines.length > 0) { address = lines[0]; } } else if (lines.length === 1) { // Single line — try to parse city/state/zip from it const csz = lines[0].match(/^(.+?)\s+([A-Za-z\s]+),\s*([A-Z]{2})\s+(\d{5}(?:-\d{4})?)$/); if (csz) { address = csz[1].trim(); city = csz[2].trim(); state = csz[3]; zip = csz[4]; } else { address = lines[0]; } } } else if (fields["contact address"]) { // Fallback to the already-stripped version address = fields["contact address"]; } // Red light is not available on the public page — needs authenticated check // We'll get it from the Playwright-based red light scraper separately const redLight = null; if (!entityName && html.includes("password")) { return { frn, entity_name: null, address: null, city: null, state: null, zip: null, status: "login_required", red_light: null, error: "CORES returned a login page for this FRN" }; } return { frn, entity_name: entityName, address, city, state, zip, status: entityName ? "found" : "not_found", red_light: redLight, error: null, }; } catch (err: any) { return { frn, entity_name: null, address: null, city: null, state: null, zip: null, status: null, red_light: null, error: err.message || "CORES unreachable" }; } } async function fetchRmdData(frn: string): Promise { // FCC RMD — the ServiceNow API requires auth, so we primarily rely on our local DB. // Try the public-facing RMD search page as a fallback to check if the FRN is listed. // The RMD public table API: https://fccprod.servicenowservices.com/api/now/table/x_g_fmc_rmd_robocall_mitigation_database const urls = [ `https://fccprod.servicenowservices.com/api/now/table/x_g_fmc_rmd_robocall_mitigation_database?sysparm_query=u_frn=${frn}&sysparm_limit=1&sysparm_fields=u_business_name,u_frn,number,u_certification_date,u_implementation_type,u_robocall_contact_name,u_robocall_contact_email`, `https://fccprod.servicenowservices.com/api/x_g_fmc_rmd/rmd?frn=${frn}`, ]; for (const url of urls) { try { const resp = await fetch(url, { signal: AbortSignal.timeout(10000), headers: { "Accept": "application/json", "User-Agent": "Mozilla/5.0 (compatible; PerformanceWest/1.0)" }, }); if (!resp.ok) continue; const data = await resp.json() as { result?: any[] }; const records = data.result || []; if (records.length === 0) continue; const rec = records[0]; return { found: true, business_name: rec.u_business_name || rec.business_name || rec.name || null, frn: rec.u_frn || rec.frn || frn, rmd_number: rec.number || rec.rmd_number || null, certification_date: rec.u_certification_date || rec.certification_date || null, implementation_type: rec.u_implementation_type || rec.implementation_type || null, contact_name: rec.u_robocall_contact_name || null, contact_email: rec.u_robocall_contact_email || null, removed: false, removal_reason: null, error: null, }; } catch { continue; } } // All API attempts failed — return not found (local DB will fill in if we have it) return { found: false, business_name: null, frn: null, rmd_number: null, certification_date: null, implementation_type: null, contact_name: null, contact_email: null, removed: false, removal_reason: null, error: "RMD public API unavailable — checked local database" }; } async function fetchLocalRmd(frn: string): Promise { try { const result = await pool.query( "SELECT * FROM fcc_rmd WHERE frn = $1 LIMIT 1", [frn], ); return result.rows[0] || null; } catch { return null; } } async function fetchLocal499Filer(frn: string): Promise { try { const result = await pool.query( "SELECT * FROM fcc_499_filers WHERE frn = $1 LIMIT 1", [frn], ); return result.rows[0] || null; } catch { return null; } } async function fetch499Detail(filerId: string): Promise<{ current_as_of: string | null; comm_type: string | null; contributor: boolean | null; hq_address: string | null; hq_city: string | null; hq_state: string | null; hq_zip: string | null; error: string | null; }> { // Scrape the FCC 499 filer detail page for filing status + address const url = `https://apps.fcc.gov/cgb/form499/499detail.cfm?FilerNum=${filerId}`; try { const resp = await fetch(url, { signal: AbortSignal.timeout(10000), headers: { "Accept": "text/html", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" }, }); if (!resp.ok) return { current_as_of: null, comm_type: null, contributor: null, hq_address: null, hq_city: null, hq_state: null, hq_zip: null, error: `499 detail returned ${resp.status}` }; const html = await resp.text(); const currentMatch = html.match(/Registration Current as of:\s*([^<]+)<\/b>/i); const current_as_of = currentMatch ? currentMatch[1].trim() : null; const commMatch = html.match(/Principal Communications Type:\s*([^<]+)<\/b>/i); const comm_type = commMatch ? commMatch[1].trim() || null : null; const contribMatch = html.match(/Universal Service Fund Contributor:\s*([^<]+)<\/b>/i); const contributor = contribMatch ? contribMatch[1].trim().toLowerCase() === "yes" : null; // Extract headquarters address (more reliable than CORES for some FRNs) const addrMatch = html.match(/Headquarters Address:\s*([^<]*)<\/b>/i); const cityMatch = html.match(/Headquarters Address:[\s\S]*?City:\s*([^<]*)<\/b>/i); const stateMatch = html.match(/Headquarters Address:[\s\S]*?State:\s*([^<]*)<\/b>/i); const zipMatch = html.match(/Headquarters Address:[\s\S]*?ZIP Code:\s*([^<]*)<\/b>/i); return { current_as_of, comm_type, contributor, hq_address: addrMatch ? addrMatch[1].trim() || null : null, hq_city: cityMatch ? cityMatch[1].trim() || null : null, hq_state: stateMatch ? stateMatch[1].trim() || null : null, hq_zip: zipMatch ? zipMatch[1].trim() || null : null, error: null, }; } catch (err: any) { return { current_as_of: null, comm_type: null, contributor: null, hq_address: null, hq_city: null, hq_state: null, hq_zip: null, error: err.message }; } } async function fetchCpniStatus(filerId: string): Promise<{ filed: boolean; cert_year: number | null; date_filed: string | null; error: string | null }> { // Check FCC CPNI portal for filing status const url = "https://apps.fcc.gov/eb/CPNI/search_results.cfm"; try { const resp = await fetch(url, { method: "POST", signal: AbortSignal.timeout(10000), headers: { "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36", }, body: `Form499FilerID=${filerId}&BeginDate=&EndDate=&ConfirmationNumbers=&SubmitForm=Find+Submissions`, }); if (!resp.ok) return { filed: false, cert_year: null, date_filed: null, error: `CPNI returned ${resp.status}` }; const html = await resp.text(); // Parse rows: Feb 28 2025 8:30PM ... 2024 // The HTML has tabs/newlines between td elements const rowRegex = /([A-Z][a-z]{2}\s+\d{1,2}\s+\d{4}[^<]*)<\/td>[\s\S]*?(\d{4})<\/td>/gi; let latestYear = 0; let latestDate = ""; let match; while ((match = rowRegex.exec(html)) !== null) { const year = parseInt(match[2]); if (year > latestYear) { latestYear = year; latestDate = match[1].trim(); } } if (latestYear > 0) { return { filed: true, cert_year: latestYear, date_filed: latestDate, error: null }; } // Check if there were no results if (html.includes("No matching records")) { return { filed: false, cert_year: null, date_filed: null, error: null }; } return { filed: false, cert_year: null, date_filed: null, error: null }; } catch (err: any) { return { filed: false, cert_year: null, date_filed: null, error: err.message }; } } async function fetchLocalRemoved(frn: string): Promise { try { const result = await pool.query( "SELECT * FROM fcc_rmd_removed WHERE frn = $1 LIMIT 1", [frn], ); return result.rows[0] || null; } catch { return null; } } /** * Search local FCC databases by FRN, 499 Filer ID, or entity name. * * GET /api/v1/fcc/search?q=Falcon+Broadband * GET /api/v1/fcc/search?frn=0027160886 * GET /api/v1/fcc/search?filer_id=812345 * * Returns matching records from fcc_rmd, fcc_499_filers, and fcc_rmd_removed. */ /** * Parse city + state from an RMD business_address string. * The address is multi-line, with the last line typically: * "Sheridan WY 82801" — City ST ZIP * "UPSALA MN 56384" — City ST ZIP * "Las Vegas NV 89104" — City ST ZIP * "Quezon City, Philippines" — international (no US state) */ function addLocationFromAddress(row: Record): Record { const addr = String(row.business_address || ""); if (!addr) return row; // Split on newlines and take the last non-empty line const lines = addr.split(/\n/).map(l => l.trim()).filter(Boolean); const lastLine = lines[lines.length - 1] || ""; // Match "City ST ZIP" or "City ST" where ST is exactly 2 uppercase letters const m = lastLine.match(/^(.+?)\s+([A-Z]{2})\s*(\d{5}(?:-\d{4})?)?$/); if (m) { row.city = m[1].replace(/,\s*$/, "").trim(); row.state = m[2]; } return row; } router.get("/api/v1/fcc/search", async (req, res) => { const q = (req.query.q as string || "").trim(); const frn = (req.query.frn as string || "").trim(); const filerId = (req.query.filer_id as string || "").trim(); if (!q && !frn && !filerId) { res.status(400).json({ error: "Provide q (name search), frn, or filer_id." }); return; } try { const results: any[] = []; if (frn) { // Exact FRN search across all tables const rmd = await pool.query( "SELECT rmd_number, frn, business_name, business_address, implementation, removed_from_rmd, removed_at, last_recertified, 'rmd' as source FROM fcc_rmd WHERE frn = $1 LIMIT 10", [frn.padStart(10, "0")], ); results.push(...rmd.rows.map(addLocationFromAddress)); const filers = await pool.query( "SELECT filer_id, frn, legal_name as business_name, state, service_type, 'filer_499' as source FROM fcc_499_filers WHERE frn = $1 LIMIT 10", [frn.padStart(10, "0")], ); results.push(...filers.rows); } if (filerId) { // Search by 499 Filer ID const filers = await pool.query( "SELECT filer_id, frn, legal_name as business_name, state, service_type, 'filer_499' as source FROM fcc_499_filers WHERE filer_id = $1 LIMIT 10", [filerId], ); results.push(...filers.rows); } if (q && q.length >= 2) { // Free-text name search across both tables const searchTerm = `%${q}%`; const rmd = await pool.query( `SELECT rmd_number, frn, business_name, business_address, implementation, removed_from_rmd, removed_at, 'rmd' as source FROM fcc_rmd WHERE business_name ILIKE $1 ORDER BY business_name LIMIT 20`, [searchTerm], ); results.push(...rmd.rows.map(addLocationFromAddress)); const filers = await pool.query( `SELECT filer_id, frn, legal_name as business_name, state, service_type, 'filer_499' as source FROM fcc_499_filers WHERE legal_name ILIKE $1 OR trade_name ILIKE $1 ORDER BY legal_name LIMIT 20`, [searchTerm], ); results.push(...filers.rows); } // Deduplicate by FRN const seen = new Set(); const deduped = results.filter(r => { const key = r.frn || r.rmd_number || r.filer_id || JSON.stringify(r); if (seen.has(key)) return false; seen.add(key); return true; }); res.json({ results: deduped, count: deduped.length, query: { q: q || undefined, frn: frn || undefined, filer_id: filerId || undefined }, }); } catch (err) { console.error("[fcc-search] Error:", err); res.status(500).json({ error: "Search failed." }); } }); /** * Search CORES / FCC 499 database by business name. * Queries the FCC 499 filer database directly for entities matching the name. * * GET /api/v1/fcc/cores-search?name=Carrier+One */ router.get("/api/v1/fcc/cores-search", async (req, res) => { const name = (req.query.name as string || "").trim(); if (!name || name.length < 2) { res.status(400).json({ error: "Provide a business name (at least 2 characters)." }); return; } try { // Query FCC 499 filer database with wildcard search // The FCC search engine can't handle & in names (treats as URL separator even when encoded) // Strip & so "AT&T" becomes "ATT" which still matches their records const sanitizedName = name.replace(/&/g, ""); const url = `https://apps.fcc.gov/cgb/form499/499results.cfm?FilerID=&frn=&LegalName=${encodeURIComponent(sanitizedName)}&state=Any+State&operational=&comm_type=Any+Type&R1=and`; const resp = await fetch(url, { signal: AbortSignal.timeout(15000), headers: { "Accept": "text/html", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" }, }); if (!resp.ok) { res.status(502).json({ error: `The FCC search system is temporarily unavailable (HTTP ${resp.status}). Try searching by FRN instead, or try again in a few minutes.` }); return; } const html = await resp.text(); // FCC sometimes returns 200 but with a maintenance/error page if (html.includes("Service Unavailable") || (html.includes("503") && !html.includes("499results"))) { res.status(502).json({ error: "The FCC search system is temporarily unavailable. Try searching by FRN instead, or try again in a few minutes." }); return; } // Parse the results table rows: 834314 ... Carrier One Inc.DBA const rowRegex = /(\d+)<\/th>\s*\n?\s*([^<]+)<\/a><\/td>([^<]*)<\/td><\/tr>/gi; const results: { filer_id: string; legal_name: string; dba: string }[] = []; let match; while ((match = rowRegex.exec(html)) !== null) { results.push({ filer_id: match[1].trim(), legal_name: match[2].trim(), dba: match[3].trim(), }); } // Also extract record count const countMatch = html.match(/(\d+)\s+Record(?:s)?\s+Found/i); const totalCount = countMatch ? parseInt(countMatch[1]) : results.length; // For each result, look up the FRN from our local database const enriched = await Promise.all(results.map(async (r) => { try { const local = await pool.query( "SELECT frn FROM fcc_499_filers WHERE filer_id = $1 LIMIT 1", [r.filer_id], ); return { ...r, frn: local.rows[0]?.frn || null }; } catch { return { ...r, frn: null }; } })); res.json({ results: enriched, count: enriched.length, total_found: totalCount, query: name, source: "FCC Form 499 Filer Database", }); } catch (err: any) { // FCC is down or blocking us — fall back to local database console.warn("[fcc-cores-search] FCC search failed, falling back to local DB:", err.message); try { const localResults = await pool.query( `SELECT filer_id, legal_name, trade_name AS dba, frn FROM fcc_499_filers WHERE legal_name ILIKE $1 OR trade_name ILIKE $1 ORDER BY legal_name LIMIT 50`, [`%${name}%`], ); const enriched = localResults.rows.map((r: any) => ({ filer_id: r.filer_id, legal_name: r.legal_name, dba: r.dba || "", frn: r.frn || null, })); res.json({ results: enriched, count: enriched.length, total_found: enriched.length, query: name, source: "Local database (FCC search temporarily unavailable)", }); } catch (dbErr: any) { console.error("[fcc-cores-search] Local fallback also failed:", dbErr); res.status(500).json({ error: "Search is temporarily unavailable. Please try again later or search by FRN." }); } } }); /** * Upload FCC Form 499 Filer Database Excel dump. * * POST /api/v1/fcc/upload-filer-db * Content-Type: multipart/form-data (file field: "file") * * Accepts the Excel dump from https://apps.fcc.gov/cgb/form499/499a.cfm * and upserts all records into fcc_499_filers table. */ router.post("/api/v1/fcc/upload-filer-db", async (req, res) => { // Accept raw body as the file content (simple approach — no multer needed) // Called via: curl -X POST -F "file=@499_dump.xlsx" .../upload-filer-db // For now, accept JSON with CSV-parsed rows from the client side const { rows } = req.body ?? {}; if (!rows || !Array.isArray(rows) || rows.length === 0) { res.status(400).json({ error: "Send JSON body with 'rows' array. Each row: {filer_id, frn, legal_name, state, service_type}", hint: "Use the CLI importer: python3 -m scripts.workers.fcc_499_filer_import /path/to/499_dump.xlsx", }); return; } try { let count = 0; for (const row of rows) { const filerId = String(row.filer_id || row.FilerID || "").trim(); const legalName = String(row.legal_name || row.LegalName || row.Company || "").trim(); if (!filerId || !legalName) continue; await pool.query( `INSERT INTO fcc_499_filers (filer_id, frn, legal_name, trade_name, state, service_type, status, last_scraped_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW()) ON CONFLICT (filer_id) DO UPDATE SET frn = COALESCE(EXCLUDED.frn, fcc_499_filers.frn), legal_name = EXCLUDED.legal_name, trade_name = COALESCE(EXCLUDED.trade_name, fcc_499_filers.trade_name), state = COALESCE(EXCLUDED.state, fcc_499_filers.state), service_type = COALESCE(EXCLUDED.service_type, fcc_499_filers.service_type), last_scraped_at = NOW(), updated_at = NOW()`, [ filerId, String(row.frn || row.FRN || row.CORESID || "").trim() || null, legalName, String(row.trade_name || row.TradeName || "").trim() || null, String(row.state || row.State || "").trim().substring(0, 2) || null, String(row.service_type || row.ServiceType || row.PrincipalCommunicationsType || "").trim() || null, String(row.status || row.OperationalStatus || "Active").trim(), ], ); count++; } const totalResult = await pool.query("SELECT COUNT(*) FROM fcc_499_filers"); res.json({ success: true, imported: count, total_in_db: parseInt(totalResult.rows[0].count), }); } catch (err) { console.error("[fcc-upload] Error:", err); res.status(500).json({ error: "Import failed." }); } }); /** * Import 499-A data from USAC E-File * * POST /api/v1/fcc/import-499a * Body: { filer_id: "812345" } * * Dispatches a worker job to scrape USAC E-File for the filer's * most recent 499-A data. Returns the extracted entity info, * service categories, and revenue lines. */ router.post("/api/v1/fcc/import-499a", async (req, res) => { const { filer_id } = req.body ?? {}; if (!filer_id || typeof filer_id !== "string" || filer_id.length < 3) { res.status(400).json({ error: "Please provide a valid USAC 499 Filer ID." }); return; } const WORKER_URL = process.env.WORKER_URL || "http://workers:8090"; try { // Dispatch to worker const workerResp = await fetch(`${WORKER_URL}/jobs`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "import_499a", filer_id: filer_id.trim(), }), signal: AbortSignal.timeout(60000), }); if (!workerResp.ok) { const err = await workerResp.text(); console.error("[fcc-import] Worker error:", err); res.status(502).json({ success: false, error: "Import service unavailable. Please try again or enter data manually.", }); return; } const result = await workerResp.json(); res.json(result); } catch (err: any) { console.error("[fcc-import] Error:", err.message); // If worker is unavailable, return a helpful message if (err.message?.includes("fetch failed") || err.message?.includes("ECONNREFUSED")) { res.status(503).json({ success: false, error: "Import service is not running. Please enter your data manually or contact us for assistance.", manual_entry_required: true, }); } else { res.status(500).json({ success: false, error: "Import failed. Please try again or enter data manually.", }); } } }); export default router;