Purpose
Search the Reno NV Accela ONE Building portal for permits created within a caller-specified date range. Return permit rows and detail links, optionally identify electrical or EZ records, and preserve visible applicant, owner, contractor, or filing-party fields so permits can be verified as homeowner- or contractor-filed without guessing from a name alone.
When to Use
Use when the caller needs recent Reno building permits, electrical permits, EZ permits, or verification of whether a permit was filed by a homeowner versus a contractor. Format inclusive dates as MM/DD/YYYY. Leave the Record Type filter unfiltered unless the caller requests a specific type. Treat a filing party as unknown when the portal does not explicitly expose an owner/homeowner or contractor role.
Workflow
- Navigate directly to
https://aca-prod.accela.com/ONE/Cap/CapHome.aspx?module=Building&TabName=Building. - After the form loads, run this evaluator with the caller's dates. It selects Reno, discovers electrical-related Record Type options, and submits the search in one interaction. Set
{record-type-value-or-empty}only when a specific option is requested; otherwise leave it empty.
(() => {
const agency = document.querySelector(
"#ctl00_PlaceHolderMain_generalSearchForm_ddlGSSubAgency",
);
const typeDD = document.querySelector(
"#ctl00_PlaceHolderMain_generalSearchForm_ddlGSPermitType",
);
const start = document.querySelector(
"#ctl00_PlaceHolderMain_generalSearchForm_txtGSStartDate",
);
const end = document.querySelector(
"#ctl00_PlaceHolderMain_generalSearchForm_txtGSEndDate",
);
const submit = document.querySelector("#ctl00_PlaceHolderMain_btnNewSearch");
if (!agency || !start || !end || !submit)
throw new Error("Accela building search form not found");
agency.value = "RENO";
start.value = "{start-date-MM/DD/YYYY}";
end.value = "{end-date-MM/DD/YYYY}";
if (typeDD && "{record-type-value-or-empty}")
typeDD.value = "{record-type-value-or-empty}";
const electricalOptions = typeDD
? [...typeDD.options]
.filter((o) => /electric|service change|solar/i.test(o.text))
.map((o) => ({ value: o.value, text: o.text.trim() }))
: [];
submit.click();
return {
submitted: true,
agency: agency.value,
start: start.value,
end: end.value,
electricalOptions,
typeDropdownId: typeDD?.id || null,
};
})();- Wait for the results page to finish loading, then run this evaluator. It extracts permit rows, detail URLs, normalized fields, explicit filing-party roles, EZ status, electrical status, and visible pagination links. Repeat the same extraction after navigating each pagination URL when the result set spans multiple pages.
(() => {
const norm = (s) => (s || "").replace(/\\s+/g, " ").trim();
const grid =
document.querySelector("#ctl00_PlaceHolderMain_dgvPermitList_gdvPermitList") ||
document.querySelector("table.ACA_GridView_OverFlow");
const rows = [...(grid || document).querySelectorAll("tr")];
const header = rows.find((r) => r.querySelectorAll("th").length);
const headers = header
? [...header.querySelectorAll("th")].map((x) => norm(x.textContent))
: [];
const permits = rows
.map((row) => {
const cells = [...row.querySelectorAll("td")].map((c) => norm(c.textContent));
const link = row.querySelector(
'a[id*="hlPermitNumber"], a[href*="CapDetail"]',
);
if (!link || !cells.length) return null;
const fields = Object.fromEntries(
cells.map((v, i) => [headers[i] || `column${i + 1}`, v]),
);
const labeled = Object.entries(fields).filter(([k]) =>
/owner|homeowner|applicant|contractor|filed by|contact/i.test(k),
);
const filingParty = labeled.map(([k, v]) => `${k}: ${v}`).join(" | ") || null;
const partyClass = /homeowner|property owner|owner\\s*\\(/i.test(
filingParty || "",
)
? "homeowner"
: /contractor|contracting company/i.test(filingParty || "")
? "contractor"
: "unknown";
const rowText = norm(row.textContent);
return {
permitNumber: norm(link.textContent),
detailUrl: link.href || null,
fields,
cells,
isEZ: /\\bEZ\\b/i.test(rowText),
electrical: /\\b(?:ELC|ELECTRICAL)\\b/i.test(rowText),
filingParty,
partyClass,
};
})
.filter(Boolean);
const pager = document.querySelector('[id*="PermitList_gdvPermitList_pager"]');
const pagination = pager
? [...pager.querySelectorAll("a")].map((a) => ({
text: norm(a.textContent),
href: a.href || null,
}))
: [];
const body = norm(document.body?.textContent);
const m = body.match(/(\\d+)\\s+Record results/i);
const resultCount = document.querySelector('[id*="lblResultCount"]');
return {
total: m ? Number(m[1]) : resultCount ? norm(resultCount.textContent) : null,
headers,
count: permits.length,
permits,
pagination,
};
})();- For an EZ permit whose results row has no explicit filing-party field, navigate to its returned
detailUrland run this evaluator on the detail page. It reads labeled tables, definition lists, and form fields, and deliberately returnsunknownwhen no explicit role is present:
(() => {
const norm = (s) => (s || "").replace(/\\s+/g, " ").trim();
const pairs = [];
document.querySelectorAll("tr").forEach((r) => {
const c = [...r.querySelectorAll("th,td")].map((x) => norm(x.textContent));
if (c.length >= 2) pairs.push([c[0], c.slice(1).join(" | ")]);
});
document.querySelectorAll("dt").forEach((dt) => {
const dd = dt.nextElementSibling;
if (dd) pairs.push([norm(dt.textContent), norm(dd.textContent)]);
});
document.querySelectorAll("label").forEach((label) => {
const el = label.htmlFor && document.getElementById(label.htmlFor);
if (el) pairs.push([norm(label.textContent), norm(el.value || el.textContent)]);
});
const relevant = pairs.filter(([k]) =>
/owner|homeowner|applicant|contractor|filed by|contact/i.test(k),
);
const filingParty = relevant.map(([k, v]) => `${k}: ${v}`).join(" | ") || null;
const partyClass = /homeowner|property owner|owner\\s*\\(/i.test(filingParty || "")
? "homeowner"
: /contractor|contracting company/i.test(filingParty || "")
? "contractor"
: "unknown";
return { filingParty, partyClass, matchedFields: relevant };
})();Site-Specific Gotchas
- The Building search form uses ASP.NET control IDs; the Reno sub-agency option is the opaque value
RENO, not the visible jurisdiction label. - Dates must be entered in
MM/DD/YYYYformat intxtGSStartDateandtxtGSEndDate; use an inclusive range according to the caller's date convention. - The Record Type dropdown is
#ctl00_PlaceHolderMain_generalSearchForm_ddlGSPermitType. Enumerate its option text and values instead of guessing opaque option values. Electrical-related options can be discovered by matchingelectric,service change, orsolar. - No demonstrated query-string equivalent exists for the agency, date, or Record Type filters; submit through the loaded form.
- Results are in
#ctl00_PlaceHolderMain_dgvPermitList_gdvPermitListor anACA_GridView_OverFlowtable; permit-number links use IDs containinghlPermitNumberor hrefs containingCapDetail. - The result list can expose a pager under an ID containing
PermitList_gdvPermitList_pager; collect its hrefs and stitch records across pages. - EZ status is visible in the row text and should be detected with a word-boundary
EZmatch. Electrical rows may be labeledELCorElectrical, so classification checks both. - Do not infer homeowner versus contractor from an individual's or company's name. Use explicit role labels on the results or detail page and report
unknownotherwise.
Expected Output
Return total, headers, count, pagination, and permits. Each permit includes permitNumber, detailUrl, normalized fields and cells, isEZ, electrical, and filingParty plus partyClass (homeowner, contractor, or unknown) when role information is visible. The form evaluator additionally returns discovered electricalOptions and typeDropdownId.