Help CenterAnalytics & Reporting Suite
    Analytics12 min read

    Analytics & Reporting Suite

    A guided tour of the four reporting surfaces in Metro2: the Data Quality dashboard, the branded PDF audit report, submission reconciliation against CRA responses, and the month-over-month trend dashboard.

    1. Overview

    The Analytics & Reporting Suite is four interconnected surfaces that share a single source of truth: the Metro2 validation rule registry. Whether you are previewing a file before submission, signing off a compliance audit, reconciling what the bureau actually accepted, or watching your data quality drift month over month, the rules and severities you see are the same ones used by the file generator.

    Each surface is designed to be useful at a different moment in your Metro 2 lifecycle:

    SurfaceWhen you use itOutput
    Data Quality dashboardBefore you cut a file — preview rule pass/fail and a weighted DQ score.Live KPI cards, failing-rule table, drill-down to offending records.
    PDF Audit ReportAfter a submission — generate a co-branded compliance artifact.Letter-size PDF stored in a private bucket; optional typed sign-off.
    Submission ReconciliationAfter the bureau returns a response file — what did they accept?Sent / accepted / rejected funnel for a specific transmission, CSV + PDF export.
    Trend dashboardMonthly compliance reviews — is data quality improving or sliding?Month-over-month DQ score line, severity mix, bureau rejection rate.

    The shared rule registry means a "critical" finding looks the same in every surface — same rule ID, same description, same severity, same weight. There is no parallel taxonomy to learn.

    What this guide covers
    Operator-level walkthroughs of each dashboard, how the Data Quality score is computed, where files are stored, what the typed attestation means, the public REST API for analytics data, and a troubleshooting section for the issues we see most often.

    2. The Data Quality Score

    Every validation run produces a single numeric score between 0.000 and 1.000. We call this the passing score. It is the headline metric on the DQ dashboard, the cover-page KPI on the PDF audit report, and the y-axis on the trend dashboard.

    How it's computed

    Each finding the validator emits is assigned a severity. Severities carry a weight; we sum the weights across the run and divide by the total number of rule evaluations performed. The score is then the complement of that ratio, clamped to [0, 1].

    weighted_findings = (critical_count × 10)
                      + (error_count    × 5)
                      + (warning_count  × 1)
                      + (info_count     × 0)
    
    passing_score = clamp01(1 − weighted_findings / total_evaluations)

    Where total_evaluations = total_records × ~30 rules. The "30 rules per record" denominator is an estimate of the average number of registry rules that apply to a single Metro 2 record under the standard preset — the DQ dashboard surfaces this denominator explicitly so you can sanity-check the math.

    Severity weights

    SeverityWeightWhat it meansBureau impact
    critical10Will almost certainly be rejected by the CRA on intake.Hard reject — the bureau drops the record entirely.
    error5Violates a documented CDIA rule; the field is not compliant.Usually accepted with errors; may suppress display fields.
    warning1Soft advisory — best practice rather than a hard rule.Bureau accepts silently; you may see downstream display oddities.
    info0Informational — surfaced for transparency, never penalized.No impact. Score is unaffected.

    What does a "passing" run look like?

    There is no single industry-standard threshold, but in practice we see these score ranges across the customer base:

    RangeLabelTypical interpretation
    0.995 – 1.000ExcellentEither zero critical findings and a handful of warnings, or a clean file. Safe to submit.
    0.970 – 0.994HealthyA few errors but no criticals. Ship it; remediate in the next cycle.
    0.900 – 0.969WatchMaterial errors present. Review the failing-rule table before you generate the file.
    Below 0.900HoldCriticals or systemic field errors — fix at the source mapping before submitting.
    Score is not pass/fail by itself
    A 0.998 score with a single critical finding is still a critical finding. Always read the severity mix below the score, not just the number on the KPI card.

    3. DQ Dashboard

    Route: /dashboard/analytics/quality

    The DQ dashboard is the day-to-day operator view. It shows the latest validation run for your active company, plus the full history of runs you can scroll back through. Every numeric KPI links to the underlying records — you should rarely need to leave this page to investigate a failing rule.

    KPI cards

    The top of the page has six KPI cards that summarize the most recent run:

    • Passing Score — the weighted score described above, formatted as a percentage with three decimals.
    • Records Evaluated — how many metro2_records rows the run touched.
    • Total Findings — sum across all severities. Clicking the card filters the findings table to the full set.
    • Critical / Error / Warning — three separate cards for the three penalized severities. Clicking each filters the findings table to that severity.

    A sample header for a healthy run might look like:

    Passing Score:    99.62%        (1.000 = perfect)
    Records Evaluated: 14,802       (444,060 checks)
    Total Findings:   168
    Critical:           0
    Errors:            12
    Warnings:         156

    Failing-rule table

    Below the KPIs is the failing-rule table. Each row is one rule from the registry that produced at least one finding in this run, sorted first by severity then by frequency. The columns are:

    ColumnDescription
    SeverityColored pill matching the weights table above.
    Rule IDPermanent registry ID, e.g. SEG_HDR_RECORD_LENGTH. Quote this in remediation tickets.
    DescriptionPlain-English rule name, plus the CDIA reference if one is on file.
    FindingsNumber of individual occurrences across all records in this run.
    RecordsNumber of distinct records that have at least one finding for this rule.
    ActionDrill-down link — opens the affected records in a filtered list view.

    Drill-down to failing records

    Clicking the drill-down chevron on any row opens the standard records view filtered to that rule ID and run ID. You can:

    • Click any record to open its full editor with the failing field highlighted.
    • Bulk-select records and apply a fix or a tag.
    • Export the filtered set to CSV (preserves the rule ID column so you can hand it to a developer).

    Kicking off a manual run

    The dashboard auto-runs validation after every file import, but you can also start one on demand. Use the Run validation button at the top right. The dialog lets you choose:

    • Scope company (all records), file (a specific import), or transmission (only what is queued for the next submission).
    • JurisdictionUS or CA. Defaults to the per-record value stored on each row; override only if you are intentionally evaluating a CA file against US rules or vice versa.
    • Max records — soft cap (default 100,000) so you don't accidentally kick off a multi-hour job on a giant portfolio.

    Runs are persisted to metro2_validation_runs with full findings in metro2_validation_findings — you never lose the audit trail by re-running.

    4. PDF Audit Report

    Route: /dashboard/analytics/reports

    The PDF audit report is a polished, co-branded compliance artifact you can hand to an auditor, an underwriter, or your own internal compliance team. It bundles a run summary, the top failing rules, the bureau reconciliation funnel (if available), and a typed attestation block into a single Letter-size PDF.

    Generating a report

    1. From the Reports page, click New audit report.
    2. Pick a validation run from the drop-down. The default is the most recent run, but you can attach a report to any historical run.
    3. Optionally attach a bureau transmission. If you do, the PDF includes the Reconciliation page (sent / accepted / rejected / top rejection codes). If you leave it blank, that page is omitted.
    4. Optionally provide a customer logo URL (see Setting your company logo).
    5. Click Generate. Rendering takes ~108 ms on a warm Lambda, plus a few hundred ms to upload the PDF to private storage. You will see the new report at the top of the list when it's ready.

    What's in the PDF

    • Cover page — Metro2 wordmark, your customer name, optional customer logo, validation-run ID, scope, and run date.
    • Data Quality Summary — the six KPI cards from the dashboard, rendered as printable cards with severity coloring.
    • Top Failing Rules — table of the 25 rules with the highest finding counts, each with its permanent rule ID and CDIA reference (when available).
    • CRA Reconciliation — sent / accepted / accepted with warnings / updated / rejected counts plus the top rejection codes from the bureau response. Only included when a transmission is attached.
    • Compliance Attestation — typed sign-off block. If the report has not yet been signed, this page shows "Awaiting sign-off." Once countersigned, regenerating the PDF stamps the attesting officer's name, title, and timestamp.
    • Every page carries the Metro2 wordmark, the customer name, page numbers, and a "confidential" footer.

    Co-branding

    The PDF is always co-branded: the Metro2 wordmark appears on every page header. When you supply a company_logo_url in the generate request, that image is placed next to the wordmark on the header bar (sized 36×36 pt) so the document reads as a Metro2 compliance artifact prepared on behalf of your company.

    PII redaction

    Record-level rows in the PDF show only the last 4 digits of the SSN — never the full SSN, never the full account number. This is intentional and not configurable: the PDF is designed to be sharable with third-party auditors who should not see raw PII.

    Past reports

    The Reports page lists every PDF ever generated for your company, newest first. Each row shows:

    • Run scope and date.
    • Transmission (bureau + date) if one was attached.
    • Sign-off status (Awaiting / Signed).
    • Download button (returns a signed URL valid for 60 minutes).
    • Sign-off button (if you haven't already attested).

    Where are PDFs stored?

    Generated PDFs live in a private Supabase Storage bucket named audit-reports. The bucket is not public; access is gated by your company's RLS policies. We hand out a signed URL each time you click Download, and those URLs expire after 60 minutes so you can't accidentally leak a long-lived link.

    File naming convention:

    audit-reports/
      {company_id}/
        {report_id}--{YYYY-MM-DD}.pdf

    5. Reconciliation Report

    Route: /dashboard/analytics/reconciliation/[transmissionId]

    Reconciliation answers a single question: "Of the records I sent the bureau, how many did they actually accept?" Every CRA returns a response file after they process your submission; we parse those responses and join them to the original records, then surface the diff as a funnel.

    The funnel

    Example funnel for a 14,802-record submission:

    StageCount% of sent
    Sent to bureau14,802100.0%
    Accepted (clean)14,64098.9%
    Accepted with warnings1180.8%
    Updated existing tradeline13,98194.5%
    Rejected440.3%

    "Updated existing" is a subset of "Accepted" — it's broken out separately because customers care a lot about whether they're creating new tradelines or refreshing ones the bureau already has.

    Top rejection codes

    Below the funnel is a "Top Rejection Codes" table — the most common reasons the bureau pushed records back. Each row has the bureau's rejection code, a sample message from one of the rejected records, and the count. Hover or click a code to expand the full list of affected records.

    Code     Sample message                                              Count
    -------- ------------------------------------------------------------ -----
    R022     Invalid date of last activity (future-dated)                    18
    R107     SSN does not match prior submission for this account            12
    R301     Account number reused across portfolios                          9
    R411     Payment history profile contains invalid character "X"           5

    CSV export and PDF

    • Export CSV — downloads the full record-level join: every sent record with its bureau status, rejection code (if any), and rejection message. Useful for piping into a remediation ticket pipeline.
    • Generate PDF — wraps the reconciliation funnel into an Audit Report PDF for this specific transmission. This is the same PDF described in section 4, but with the transmission pre-attached.
    What's a transmission?
    A transmission is a single bureau-bound file delivery — one Metro 2 file sent to one bureau on one date. If you furnish to all four CRAs monthly, you'll have four transmissions per cycle, each with its own reconciliation page.

    6. Trend Dashboard

    Route: /dashboard/analytics/trends

    The trend dashboard turns months of validation runs and bureau responses into a long-view picture of your program's compliance trajectory. Three charts on one page:

    The three charts

    • DQ score over time — a line chart of the monthly average passing score for the last 12 months. Hovering shows the month's run count and total findings.
    • Severity mix — a stacked bar (critical / error / warning / info) for each month. This is where you'll spot a sudden spike in criticals even if the score is still healthy.
    • Bureau rejection rate — line chart of rejected / sent per month, optionally split per bureau (Equifax, Experian, TransUnion, Innovis).

    Per-portfolio vs company-wide

    A toggle at the top of the page switches between:

    • Company-wide — every record, every portfolio, aggregated. This is the default and the right view for monthly executive reviews.
    • Per-portfolio — a small-multiples grid with one mini-chart per portfolio. Use this when you furnish multiple programs (e.g. auto loans + BNPL + revolving) from one company account and want to know which line of business is dragging the score down. See Multi-Portfolio Routing for how portfolios are set up.

    Exporting the trend

    Both charts have an Export dropdown:

    • CSV — one row per month per portfolio (or per company in company-wide mode), with all three metrics. Useful for BI tools or board-deck spreadsheets.
    • PNG — chart snapshot for slides.
    • PDF — a "Trend Summary" PDF that wraps all three charts and a short narrative.
    Sample month-over-month row
    month     dq_score   runs   critical   error   warning   sent      rejected   reject_rate
    2026-01   0.9912     11     0          47      612       142,118   411        0.29%
    2026-02   0.9941     11     0          22      503       143,902   298        0.21%
    2026-03   0.9876     12     3          71      809       147,560   480        0.33%
    2026-04   0.9950     12     0          18      466       148,201   265        0.18%

    7. Where the data comes from

    Validation rule registry

    Every finding in every dashboard traces back to a row in metro2_validation_rules. We ship 56 system rules covering every required Metro 2 segment (Header, Base, J1, J2, K1–K4, L1, N1, Trailer) plus the most common CDIA-defined consistency rules. Each rule has:

    • A permanent rule ID — e.g. BASE_DATE_OPENED_FUTURE. The ID is stable across releases, so if you write a remediation playbook against a rule it won't break when we ship new rules.
    • A severity — assigned by us based on bureau documentation and observed rejection behavior. Severity drives the score weights.
    • A CDIA reference — the section number in the CDIA Credit Reporting Resource Guide, where applicable.
    • A category — Field Format, Cross-field Consistency, Date Logic, Segment Required, etc. Used for grouping in the failing-rule table.

    Adding a new rule (or re-leveling an existing rule's severity) is a release event — the rule registry is versioned alongside the application. Customer-specific rule overrides are on the roadmap but not yet available.

    Materialized view refresh

    Trend-dashboard performance is backed by three materialized views:

    MatviewGranularityWhat it stores
    metro2_dq_monthlymonth × companyAverage DQ score, total findings, severity counts.
    metro2_run_monthlymonth × company × scopeRun counts, records evaluated, total evaluations.
    metro2_transmission_monthlymonth × bureau × companySent, accepted, rejected, top rejection codes.

    These are refreshed nightly at 02:00 UTC by a Vercel cron job (/api/cron/refresh-dq-monthly). That means the trend dashboard always lags real-time by up to 24 hours — if you import a file at 10:00 UTC today, you'll see it in the company-wide trend tomorrow morning. The DQ dashboard and PDF report are live and do not depend on the matviews.

    CRA response files

    Accepted-vs-rejected numbers come from the bureau response files we ingest after each transmission. Equifax, Experian, TransUnion, and Innovis each have slightly different response formats; we normalize them into a common shape on import and join them to your sent records by account number + SSN hash. See the CRA Responses guide for the ingestion details and how to manually re-process a response file.

    Today, the company logo for the PDF audit report is passed per-request in the body of the generate call:

    POST /api/analytics/reports/audit-pdf
    {
      "run_id": "9b25...c4",
      "transmission_id": "b71f...92",
      "company_logo_url": "https://cdn.example.com/acme-logo.png"
    }

    The "New audit report" dialog also exposes a logo URL field that maps to this same parameter. If you omit it, the cover page renders without a customer logo — just the Metro2 wordmark.

    Logo guidelines
    • Use a square image at least 144×144 px. We render the logo at 36×36 pt (~48 px at 96 DPI) on the page header and 96×96 pt on the cover hero.
    • PNG with transparency works best. JPEG is fine. SVG is not supported by @react-pdf/renderer.
    • The URL must be publicly reachable from our render workers — no private CDN, no signed URLs, no localhost. If the fetch fails, the PDF still renders but the logo slot is empty (we log a warning).

    A permanent "company logo" upload field on the company settings page — which would default into the generate request — is on the roadmap but not yet shipped. Until then, the recommended pattern is to host your logo at a stable URL (your marketing site CDN works fine) and either paste it into the dialog or have your integration include it in the API call.

    9. Typed attestation

    The last page of every audit-report PDF is a Compliance Attestation block. It exists so a designated officer at your company can put their name to the run — confirming that the data quality findings accurately represent the file at the time of generation.

    What "typed" attestation means

    We use a typed sign-off rather than electronic-signature (DocuSign, Adobe Sign, etc.) on purpose. The FCRA does not mandate an electronically-signed compliance artifact for furnisher data quality reports, and most of our customers' internal compliance programs are satisfied with a named, dated, role-stamped attestation. The PDF page records:

    • Name — the typed full name of the attesting officer.
    • Title — e.g. "VP, Credit Operations" or "Compliance Manager".
    • Timestamp — the UTC moment the sign-off was submitted via the console.

    The block on the PDF itself contains the disclaimer text: "This is a typed attestation per the customer's compliance program; FCRA does not mandate electronic signature for furnisher data quality reports."

    Who can sign

    Any user with the compliance_signer role on your company can attest. By default, company owners and admins have this role; you can grant it to other users from Settings → Team. The sign-off action is recorded in the audit log with the signer's user ID, the run ID, the report ID, and the IP address of the request.

    Audit log retention

    Sign-off events live in the company audit log forever — they are never purged. The PDF files themselves are also retained indefinitely in the audit-reports bucket. If you need to re-render a signed report (for example, because you updated the company logo and want the new branding on a historical artifact), use the Regenerate action on the report row; the new PDF will carry the same attestation block but the refreshed cover.

    Sign-off is not reversible
    Once a report is signed, the attestation cannot be removed — regeneration replaces the file but preserves the same signed record. If the wrong person signed by accident, generate a new report against the same run rather than trying to "undo" the original.

    10. Common questions

    Why is my DQ score X when I expected Y?
    Almost always the answer is severity mix. Two runs with the same total finding count can produce wildly different scores — one critical weighs the same as ten warnings. Open the failing-rule table and look at the top rows: if they're orange/red, the score is low for a reason. If they're yellow and the score still looks low, the denominator may be smaller than you think (a small file with even a few errors will score worse than a large file with the same number of errors, because the math divides by total evaluations).
    How fresh is the trend dashboard?
    The trend dashboard reads from materialized views refreshed nightly at 02:00 UTC by a Vercel cron job (/api/cron/refresh-dq-monthly). The current day's runs are not yet in the trend — they show up the next morning. If you need a same-day view, use the DQ dashboard, which reads live from metro2_validation_runs.
    Can I export the trend data?
    Yes — every chart has an Export dropdown with CSV, PNG, and PDF options. The CSV export is the one we recommend if you're plumbing the data into a BI tool; it includes one row per month per portfolio (or per company in company-wide mode) with all three metric columns. See section 6 above.
    Why do I see 0 rules failed but my submission still got rejected?
    The DQ dashboard runs a subset of all possible Metro 2 checks — the 56 rules in the registry shipped with this release. The bureaus run their own much larger rule sets that include lender-specific consistency checks, account-number reuse detection across the full national bureau database, and dispute-state cross-references that we have no visibility into. If you want the strictest pre-submission validation we offer, use the full validation mode in Multi-Portfolio Routing, which is closer to the bureau's behavior.
    What does 'total_evaluations' mean? Is it really 30 rules per record?
    It's an estimate. The Metro 2 validator runs about 30 distinct checks against a typical Base + J1 + J2 record under the standard preset; the exact number varies by segment composition and jurisdiction (US vs CA). We surface the denominator in the dashboard and the PDF so the math is transparent, but the weighted score is what you should actually use to compare runs. Comparing raw finding counts across runs of different sizes is misleading.
    Can I delete a validation run or a PDF?
    Validation runs and signed PDFs are immutable by design — they're compliance artifacts. You can hide a run from the default DQ dashboard list by archiving it (archived_at column), but the row stays in the database forever and the underlying findings table is never purged. PDFs in the audit-reports bucket can be redacted if they contain something genuinely sensitive — contact support.
    Do I need to attest every report?
    No. Attestation is optional and most operational reports are never signed. We recommend signing only the reports that go to external auditors, your board, or your compliance committee. The unsigned PDFs are perfectly fine for internal-only use.
    My run summary shows critical = 0 but the score is 0.91. What gives?
    Probably a lot of errors (weight 5) in a relatively small file. Remember that weighted findings = 10 × critical + 5 × error + 1 × warning. With zero criticals but ~180 errors in a 1,000-record file, you get 900 / 30,000 = 0.03, so the score comes out around 0.97. If the file is even smaller, the score drops further.

    11. API reference

    Every dashboard surface is backed by a REST endpoint under /api/analytics/*. All endpoints accept either a session cookie (when called from the console) or a bearer API key (Authorization: Bearer YOUR_KEY). All responses are JSON. All routes are scoped to your company by the auth context — you cannot read another company's runs.

    DQ runs

    Method + PathPurposeNotable params / fields
    GET /api/analytics/dq-runsList recent validation runs for your company.scope, limit, cursor
    POST /api/analytics/dq-runsKick off a new validation run.Body: scope, file_upload_id, metro2_jurisdiction, max_records
    GET /api/analytics/dq-runs/{id}Fetch a single run's summary (score, counts, scope, ran_at).
    GET /api/analytics/dq-runs/{id}/findingsStream findings for a run, paginated and filterable.severity, rule_id, limit, cursor

    Sample POST response:

    {
      "run": {
        "id": "9b25e418-...-c4",
        "company_id": "f01a...",
        "scope": "company",
        "ran_at": "2026-05-17T19:42:11Z",
        "total_records": 14802,
        "total_evaluations": 444060,
        "passing_score": 0.9962,
        "critical_count": 0,
        "error_count": 12,
        "warning_count": 156,
        "info_count": 0
      }
    }

    Reconciliation

    Method + PathPurpose
    GET /api/analytics/reconciliation/{transmission_id}Returns the sent/accepted/rejected funnel + top rejection codes for one transmission.
    {
      "transmission": {
        "id": "b71f...92",
        "bureau": "equifax",
        "transmission_date": "2026-05-01",
        "records_count": 14802,
        "status": "responded"
      },
      "reconciliation": {
        "sent": 14802,
        "accepted": 14640,
        "warning": 118,
        "updated": 13981,
        "rejected": 44,
        "topRejectionCodes": [
          { "code": "R022", "count": 18, "sampleMessage": "Invalid date..." },
          { "code": "R107", "count": 12, "sampleMessage": "SSN does not match..." }
        ]
      }
    }

    Audit PDF

    Method + PathPurpose
    POST /api/analytics/reports/audit-pdfGenerate a new PDF audit report. Synchronous, returns the report row.
    GET /api/analytics/reports/audit-pdfList past reports for your company.
    POST /api/analytics/reports/audit-pdf/{id}/sign-offRecord a typed attestation against a report.

    Sample generate request:

    curl -X POST https://metro2.switchlabs.dev/api/analytics/reports/audit-pdf \
      -H "Authorization: Bearer $METRO2_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "run_id": "9b25e418-...-c4",
        "transmission_id": "b71f...92",
        "company_logo_url": "https://cdn.example.com/acme-logo.png"
      }'

    Sample sign-off request:

    curl -X POST \
      https://metro2.switchlabs.dev/api/analytics/reports/audit-pdf/abc-123/sign-off \
      -H "Authorization: Bearer $METRO2_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "signed_off_by_name": "Jane Smith",
        "signed_off_by_title": "VP, Credit Operations"
      }'

    Trend

    Method + PathPurpose
    GET /api/analytics/trendReturns month-over-month rows for the trend dashboard.

    Supported query params:

    • months — how many months back to return (default 12, max 36).
    • portfolio_id — restrict to a single portfolio. Omit for the company-wide view.
    • bureau — restrict the rejection-rate series to one CRA (equifax, experian, transunion, innovis).
    • formatjson (default) or csv.
    GET /api/analytics/trend?months=6&format=json
    
    {
      "trend": [
        {
          "month": "2026-01",
          "dq_score": 0.9912,
          "runs": 11,
          "critical": 0, "error": 47, "warning": 612, "info": 9,
          "sent": 142118, "rejected": 411
        },
        ...
      ]
    }
    Rate limits
    Analytics endpoints are subject to the same per-API-key rate limit as the rest of the Metro2 API (currently 60 requests per minute per key). The trend endpoint is materialized-view-backed so it's cheap; the audit-pdf POST is the most expensive call (~120–200 ms wall clock) and you should not call it in a tight loop.

    12. Troubleshooting

    The trend dashboard shows stale data

    The most likely cause is that the nightly cron didn't run, or ran but failed.

    • Check the dashboard's "Data freshness" badge in the top-right corner. If it reads more than 26 hours old, the cron has missed at least one cycle.
    • Look at the most recent run for the cron endpoint /api/cron/refresh-dq-monthly in Vercel logs. A non-200 response is the smoking gun.
    • As a workaround, an admin can hit the cron endpoint manually: curl -X POST https://metro2.switchlabs.dev/api/cron/refresh-dq-monthly -H "Authorization: Bearer $CRON_SECRET". The endpoint is idempotent.
    • If the cron is running but the data is still stale, the matviews may be locked by a long-running query. Contact support.

    PDF render fails

    PDF rendering is fast (~108 ms warm) but it can fail for two main reasons:

    • Unreachable logo URL. If company_logo_url 404s, times out, or returns a non-image content type, the PDF still renders but the logo slot is empty and a warning is logged. If you supplied a URL and don't see the logo, check it from a browser first.
    • Run has no findings AND no records. An empty run will render a degenerate PDF (cover + "No findings recorded" placeholder). If you see a request 500 with run_not_found, the run ID you passed doesn't belong to your company.
    • Cold-start timeout. Rare, but possible on serverless cold starts. Retry once. If it persists, contact support with the request ID.

    Reconciliation shows fewer accepted than sent

    This is usually a CRA-response-file processing lag rather than an actual problem with the submission. The bureaus return their response files on their own schedule (typically 24–72 hours after they process the submission), and we ingest them as they arrive.

    • Look at the transmission's status field. If it's sent rather than responded, the bureau hasn't returned a response file yet — the funnel is reflecting a partial picture.
    • If the status is responded but the "accepted" number is lower than expected, the response file may have only acknowledged a subset of the submission. Open the CRA Responses guide for the per-bureau quirks (Experian, for example, sometimes sends two response files: one for accepted records and a follow-up for rejected).
    • As a last resort, re-ingest the response file from the CRA Responses page; it's idempotent and will reconcile what we have on disk.

    DQ run shows 0 records evaluated

    Almost always one of:

    • You ran the validation against a file upload ID that has no records (the file failed to import). Check /dashboard/uploads for the file's status.
    • You selected scope transmission and there's nothing queued for the next transmission yet. Select company instead.
    • The company truly has zero records imported. The dashboard's empty state should make this obvious — if it doesn't, you may have a company-context mismatch (check the company switcher in the top nav).

    Analytics API returns 403 with a valid key

    Your API key is scoped to a single company, but the resource you're asking for (a run ID, a transmission ID, a report ID) belongs to a different company. The 403 is intentional — keys cannot cross company boundaries even if your user account has access to both. Generate a separate key for the other company under Settings → API Keys.

    If your question isn't answered here, reach out via the contact form — include the run ID, transmission ID, or report ID and we can usually root-cause within a few minutes.

    Still stuck? Contact support or browse the Help Center.