Retrieve Bid Documents from Excelerator Takeoff

Site exceleratortakeoff.comTask retrieve-plan-service-bid-documentsVersion v2Updated Aug 18, 2026Category browser-automation

Resolve a bid project through Online Plan Service and extract its document tree from the authenticated Excelerator Takeoff viewer or embedded document metadata. This skill was captured from a live agent session on exceleratortakeoff.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

Retrieve bid-plan documents from Excelerator Takeoff/Online Plan Service instead of Cal eProcure, using a project name, search phrase, or known project identifier and returning document paths, URLs, sizes, page counts, and likely instruction/pre-bid document candidates.

When to Use

Use when the caller needs plans, specifications, notices, instructions, addenda, or other bid documents for an Online Plan Service project. The caller may provide a project name or search phrase without the site's opaque project identifier, or may already provide the identifier.

Workflow

  1. Open https://login.onlineplanservice.com/Login.aspx. If the consent dialog appears, accept it, then authenticate with credentials supplied by the caller or the existing browser session; never hard-code credentials.
  2. If only a project name or search phrase is available, resolve the opaque project ID with one authenticated request to: https://login.onlineplanservice.com/ajax_grid_datasource.aspx?mode=biddingprojects&search={URL-encoded query}&page=1&sort=biddate&dir=ASC Parse the JSON response, select the record whose project name/title best matches {project-name}, and read its project identifier, such as a VOPS... code. If more pages are indicated, request subsequent page={n} values until the intended match is found. Do not guess the identifier.
  3. Prefer the direct embedded-data document page when the project ID is known: https://login.onlineplanservice.com/DocumentViewer.aspx?project={project-id} Navigate there in the authenticated context and run the evaluator below once after the page loads. This page embeds window.data, including project metadata and the hierarchical FancyNodeData document tree.
  4. If the embedded-data page is unavailable or lacks the document tree, navigate directly to: https://exceleratortakeoff.com/Viewer/{project-id}/index Wait for #tree to populate and run the Fancytree evaluator below. Preserve the authenticated browser session across the login and viewer origins.
  5. To find mandatory job-walk language, inspect or download the returned candidate documents whose titles or paths match terms such as instruction, notice, bidder, prebid, pre-bid, mandatory, conference, walk, general condition, or Division 00. Treat these as candidates rather than assuming a matching filename proves the language is mandatory; extract the actual wording from the document when requested.

Embedded document-page evaluator (run on the loaded DocumentViewer.aspx page):

(() => {
  const clean = v => String(v ?? '').replace(/<!--!-->/g, '').trim();
  const html = document.documentElement.outerHTML;
  const match = html.match(/window\\.data\\s*=\\s*(\\{[\\s\\S]+?\\});/);
  if (!match) return { projectId: new URL(location.href).searchParams.get('project'), documents: [], error: 'Embedded window.data was not found' };
  let data;
  try { data = JSON.parse(match[1]); } catch (e) { return { projectId: new URL(location.href).searchParams.get('project'), documents: [], error: 'Unable to parse embedded project data: ' + e.message }; }
  const documents = [];
  const walk = (node, parents) => {
    if (!node) return;
    const title = clean(node.Title);
    const path = title ? parents.concat(title) : parents;
    const item = node.Data || node.data || {};
    if (node.Folder === false && item.Url) {
      const folder = path.slice(0, -1).join('/');
      const titleText = title;
      documents.push({ path: folder, title: titleText, url: new URL(item.Url, location.href).href, size: item.Size ?? null, pages: item.Pages ?? null, likelyRelevant: /instruction|notice|bidder|prebid|pre-bid|mandatory|conference|walk|general condition|division\\s*0/i.test(folder + ' ' + titleText) });
    }
    for (const child of (node.Children || [])) walk(child, path);
  };
  walk(data.FancyNodeData, []);
  return { projectId: data.Opsplannum || new URL(location.href).searchParams.get('project'), projectName: data.ProjectName || null, projectDetails: data.ProjectDetails || null, documents };
})()

Fancytree fallback evaluator (run on the loaded /Viewer/{project-id}/index page):

(() => {
  const clean = v => String(v ?? '').replace(/<!--!-->/g, '').trim();
  const treeEl = document.querySelector('#tree');
  if (!treeEl || !window.jQuery || !window.jQuery.fn.fancytree) return { projectId: location.pathname.match(/\\/Viewer\\/([^/]+)/i)?.[1] || null, documents: [], error: 'Fancytree document tree is not available' };
  const tree = window.jQuery(treeEl).fancytree('getTree');
  const documents = [];
  const pathOf = node => { const parts = []; for (let p = node.parent; p && p.title; p = p.parent) parts.unshift(clean(p.title)); return parts.join('/'); };
  tree.visit(node => {
    const content = node.data && node.data.content;
    if (content && content.Url) {
      const path = pathOf(node), title = clean(node.title);
      documents.push({ path, title, url: new URL(content.Url, location.href).href, size: content.Size ?? null, pages: content.Pages ?? null, likelyRelevant: /instruction|notice|bidder|prebid|pre-bid|mandatory|conference|walk|general condition|division\\s*0/i.test(path + ' ' + title) });
    }
  });
  return { projectId: location.pathname.match(/\\/Viewer\\/([^/]+)/i)?.[1] || null, documents };
})()

Site-Specific Gotchas

  • The project identifier is opaque; do not guess it from the project name. Resolve it through the authenticated ajax_grid_datasource.aspx search endpoint first.
  • Authentication and consent occur on login.onlineplanservice.com, while the final viewer is on exceleratortakeoff.com; preserve the same browser session when navigating between them.
  • DocumentViewer.aspx?project={project-id} embeds window.data containing Opsplannum, project metadata, and the recursive FancyNodeData tree; this is a faster extraction path than clicking through the project grid.
  • In embedded data, leaf documents have Folder === false and Data.Url; folder nodes generally lack a document URL and must be excluded.
  • In the Excelerator viewer, the tree is mounted at #tree and may require a short load wait before evaluation. Its useful metadata is in each Fancytree node's data.content, especially Url, Size, and Pages.
  • Filename/path matching only identifies likely documents. Mandatory job-walk requirements must be verified from the document text itself, not inferred solely from a title.

Expected Output

Return the resolved project ID and a documents array. Each document contains its folder path, displayed title, downloadable URL, size, page count, and a likelyRelevant flag for instruction, notice, bidder, pre-bid, mandatory-walk, conference, or related documents. Include project name/details when available. Return an empty array only when the authenticated source has no document nodes.

Call it

GET https://production-sfo.browserless.io/skills?token=TOKEN-HERE&domain=exceleratortakeoff.com&task=retrieve-plan-service-bid-documents