Find Recent Electrical Building Permits

Site aca-prod.accela.comTask find-recent-electrical-building-permitsVersion v4Updated Jul 28, 2026Category browser-automation

Search Reno Accela building permits over a caller-specified date range, extract permit records, and optionally identify electrical or EZ permits and visible homeowner or contractor filing-party fields. This skill was captured from a live agent session on aca-prod.accela.com and publishes here verbatim, exactly as an agent receives it.

NoteSelectors and URL schemes drift as sites change. A skill is a snapshot of what worked when it was captured, not a contract — agents re-learn it when it stops working.

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

  1. Navigate directly to https://aca-prod.accela.com/ONE/Cap/CapHome.aspx?module=Building&TabName=Building.
  2. 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,
  };
})();
  1. 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,
  };
})();
  1. For an EZ permit whose results row has no explicit filing-party field, navigate to its returned detailUrl and run this evaluator on the detail page. It reads labeled tables, definition lists, and form fields, and deliberately returns unknown when 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/YYYY format in txtGSStartDate and txtGSEndDate; 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 matching electric, service change, or solar.
  • 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_gdvPermitList or an ACA_GridView_OverFlow table; permit-number links use IDs containing hlPermitNumber or hrefs containing CapDetail.
  • 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 EZ match. Electrical rows may be labeled ELC or Electrical, 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 unknown otherwise.

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.

Call it

GET https://production-sfo.browserless.io/skills?token=TOKEN-HERE&domain=aca-prod.accela.com&task=find-recent-electrical-building-permits