# Spec Book to Submittal Register (CSI MasterFormat, Procore) (`hanielazevedo/spec-book-submittal-register`) Actor

Spec book to submittal register: turn a construction project manual PDF into a CSI MasterFormat section index and a submittal register with paragraph references (1.3.A) and page numbers, plus a Procore import CSV. Rules-based, no AI. $0.05 per spec page; failed books free.

- **URL**: https://apify.com/hanielazevedo/spec-book-submittal-register.md
- **Developed by:** [Haniel Azevedo](https://apify.com/hanielazevedo) (community)
- **Categories:** Automation, Developer tools, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 validated spec pages

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Spec Book to Submittal Register (CSI MasterFormat → Procore CSV)

Upload a construction **spec book / project manual PDF** and get, in about a second per 500 pages:

1. **CSI MasterFormat section index**: every section with number, title, division and page range.
2. **Submittal register**: every submittal required in PART 1 of each technical section: product data, shop drawings, samples, O\&M data, warranties, certificates, test reports, qualification data, LEED/CHPS documentation, mix designs, and more. Each one comes with its **paragraph reference (e.g. 1.3.A)** and **page number**, so you can check it against the spec.
3. **Procore-ready import CSV**, using the exact column headers of Procore's Submittals import template.

The extraction is **rules-based: no AI, no LLM, no third-party APIs**. Your spec book is not sent anywhere, and the same input always gives the same output.

### Who it is for

General contractors, subcontractors, project engineers and submittal coordinators who build submittal logs by hand at the start of every project. A 500-page project manual usually takes a day or more to log by hand. This Actor gives you a first-pass register to review in minutes.

### Example input

The Console form is prefilled with a real public spec book: the 380-page project manual for the Story County (Iowa) Justice Center Remodel. Click **Start** without changing anything:

```json
{
  "pdfUrls": [
    "https://www.storycountyiowa.gov/DocumentCenter/View/16034/2024-09-11-Project-Manual-SC-Justice-Center-Remodel?bidId="
  ]
}
```

### Example output

That run takes about 6 seconds, finds 82 CSI sections (79 validated) and returns **171 submittal rows**. The demo book is free. One register row (run of 1 Nov 2026, trimmed):

```json
{
  "recordType": "submittal",
  "sectionNumber": "03 30 00",
  "sectionTitle": "Cast-In-Place Concrete",
  "division": "03",
  "divisionName": "Concrete",
  "article": "1.04 SUBMITTALS",
  "paragraphRef": "1.04.B",
  "page": 66,
  "submittalNumber": "033000-01",
  "title": "Product Data",
  "submittalType": "product data",
  "category": "Action",
  "procoreSubmittalType": "Product Information",
  "description": "Submit manufacturers' data on manufactured products showing compliance with specified requirements and installation instructions."
}
```

The `OUTPUT` record summarises each book and links the `SUBMITTAL_REGISTER.csv`, `PROCORE_IMPORT.csv` and `SECTION_INDEX.csv` files:

```json
{
  "fileName": "2024-09-11 Project Manual SC Justice Center Remodel.pdf",
  "status": "ok",
  "pageCount": 380,
  "sectionsFound": 82,
  "sectionsValidated": 79,
  "submittals": 171,
  "pagesCharged": 0,
  "demo": true
}
```

### Input

| Field | What it does |
|---|---|
| `pdfUrls` | Public links to spec book PDFs. |
| `pdfStore` + `pdfKeys` | Or pick one of your Apify key-value stores holding the PDFs (read-only access, granted only to that store). |
| `includeDivision01` | Also mine Divisions 00–01 (off by default: those sections describe procedures, not product submittals). They are always listed in the section index. |
| `includeWarranty` | Add special, manufacturer and installer warranties as closeout submittals (on by default). |
| `sectionNumberFormat` | `08 71 00`, `087100`, `08.71.00` or as printed. Match your Procore Specifications tool. |
| `procoreSubmittalManager` | Email written into the Procore CSV (Procore requires a Submittal Manager). |
| `procoreSubmittalStatus` | Optional Procore status. Leave it blank to get Procore's default (Draft). |
| `maxPages`, `maxFileSizeMb` | Safety limits. |

### Output

**Dataset**: one row per submittal (`submittalNumber`, `sectionNumber`, `sectionTitle`, `title`, `submittalType`, `procoreSubmittalType`, `category` Action/Informational/Closeout, `paragraphRef`, `page`, `article`, `description`), plus one `recordType: "error"` row for each book that failed.

**Key-value store** (one set per book, prefixed `book1-<name>-`):

- `SECTION_INDEX.json` / `.csv`: Section Number, Section Title, Division, Division Name, Start Page, End Page, Page Count, Validated, Submittal Count.
- `SUBMITTAL_REGISTER.json` / `.csv`: the full register.
- `PROCORE_IMPORT.csv`: Package Title, Package Spec Section Number, Package Spec Section Description, Package Number, Submittal Title, Submittal Spec Section Number, Submittal Spec Section Description, Submittal Number, Description, Submittal Manager, Submittal Status, Submittal Type, Location, Received Date, Issue Date, Submit By Date, Responsible Contractor Name. There is one package per spec section. Types are mapped to Procore's default types (Product Information, Shop Drawing, Sample, Product Manual, Plans, Document, Other), and the description ends with the paragraph and page reference.
- `OUTPUT`: a run summary with links to every file, warnings and the pages charged.

Example register row:

```json
{"submittalNumber": "102113-01", "sectionNumber": "10 21 13", "sectionTitle": "Toilet Compartments",
 "title": "Product Data", "submittalType": "product data", "procoreSubmittalType": "Product Information",
 "category": "Action", "paragraphRef": "1.03.A", "page": 43, "article": "1.03 ACTION SUBMITTALS",
 "description": "For each type of product indicated. Include construction details, material descriptions, ..."}
```

### Accuracy (measured)

We tested on four public project manuals: Story County (IA) Justice Center, 380 pages; UNLV, 514 pages; Erie County (NY) District Attorney lobby, 530 pages; and a 47-page school package.

- **Section detection**: 203 of 203 table-of-contents sections were found, with no false sections.
- **Submittal items**, against 39 hand-checked sections:
  - Development set (25 sections, used to write the rules): precision 100%, recall 99.2%, type accuracy 100%.
  - **Held-out set** (14 sections, labelled after the rules were frozen): **precision 98.6%, recall 98.6%, type accuracy 100%**.
- The rules prefer precision. A paragraph that states a requirement but names no deliverable (for example "Provide message list" with no label) is left out rather than guessed. Always review the register before you issue it.

### Limits

- Text PDFs only. Scanned pages are **not OCRed** (they are reported in the warnings).
- Built for CSI 3-part sections (SECTION header … PART 1 GENERAL … END OF SECTION), in MasterFormat 2004+ (6-digit) or 1995 (5-digit) numbering. Sections without a clean header/end pair are listed in the index but not mined.
- If a spec has no list letters in its text layer, the letters are inferred from indentation; those rows are flagged `paragraphRefInferred: true`.

### Pricing

Pay per event: **$0.05 per `spec-page` event, one for each page of the validated CSI sections returned**. You are charged only after the register has been saved, and only if it has at least one submittal. **Failed downloads, unreadable or scanned PDFs, books with no recognisable sections or no submittals, and the prefilled demo book are free.** If your max run cost is reached, the output stops at the last whole section that fits, and you pay only for those pages.

### Errors

`INVALID_URL`, `BLOCKED_URL`, `DNS_ERROR`, `DOWNLOAD_FAILED`, `DOWNLOAD_TIMEOUT`, `NOT_FOUND`, `HTTP_ERROR`, `ACCESS_DENIED`, `FILE_TOO_LARGE`, `EMPTY_FILE`, `UNSUPPORTED_FORMAT`, `CORRUPT_FILE`, `ENCRYPTED_PDF`, `NO_SECTIONS_FOUND`, `BUDGET_EXCEEDED`. Warnings: `PAGES_TRUNCATED`, `LOW_TEXT_YIELD`, `SECTIONS_NOT_VALIDATED`, `NO_SUBMITTALS_FOUND`, `BUDGET_PARTIAL_OUTPUT`.

### Privacy and security

The Actor runs with limited permissions. It can read only the key-value store you select, and it downloads only public http(s) URLs (private/internal addresses are blocked). Nothing is sent to third parties. "Procore" and "MasterFormat" are trademarks of their owners. This Actor is not affiliated with Procore Technologies or CSI.

### Other Actors by Haniel

- [Floor Plan Takeoff: PDF Room Areas, Wall LF & Door Counts](https://apify.com/hanielazevedo/floor-plan-takeoff): Construction takeoff from vector floor-plan PDFs: room square footage, wall linear feet and door counts, with a scale self-check. $2.50 per sheet.
- [ACORD 25 COI Checker: Certificate of Insurance Compliance](https://apify.com/hanielazevedo/acord-25-coi-checker): Read ACORD 25 certificate of insurance PDFs into JSON and get PASS / FAIL / REVIEW against your limits, expiry and additional insured rules. $0.08 per certificate.
- [SAM.gov Scraper: Federal Contract Opportunities Monitor](https://apify.com/hanielazevedo/samgov-contract-opportunities): New SAM.gov contract opportunities, RFPs and sources sought by NAICS, set-aside and agency. No API key. $2 per 1,000 notices.
- [PDF to Markdown Converter & PDF Text Extractor (OCR, DOCX)](https://apify.com/hanielazevedo/document-to-markdown-ocr): PDF, scanned PDF, DOCX, HTML and images to clean Markdown or text for RAG and AI agents. $3 per 1,000 documents.

# Actor input Schema

## `pdfUrls` (type: `array`):

Public http(s) links to spec books / project manuals (text PDFs exported from Word, MasterSpec, BSD SpecLink, etc.; scanned PDFs are not OCRed). To use files from your Apify account, put them in a key-value store and select it below: this Actor runs with limited permissions and cannot read a store you have not selected. The prefilled public demo book (Story County, Iowa, 380 pages) is processed free of charge.

## `pdfStore` (type: `string`):

Your Apify key-value store holding the spec book PDFs (Console > Storage > Key-value stores). Selecting it grants this run read-only access to that one store. Without record keys, every record whose key ends in ".pdf" is processed.

## `pdfKeys` (type: `array`):

Optional. Keys of the PDF records in the selected store, e.g. "project-manual.pdf". Leave empty to use every "\*.pdf" record.

## `includeDivision01` (type: `boolean`):

Divisions 00 (procurement/contracting) and 01 (general requirements) are always listed in the section index. By default their submittal paragraphs are not put in the register, because they describe procedures (submittal schedule, formats) rather than product submittals. Turn on to include them.

## `includeWarranty` (type: `boolean`):

Add special/manufacturer/installer warranties from the WARRANTY article of PART 1 as closeout submittals.

## `sectionNumberFormat` (type: `string`):

How section numbers are written in the outputs. Procore imports need the number exactly as it appears in your project's Specifications tool, so pick the matching format.

## `procoreSubmittalManager` (type: `string`):

Optional. Email of the Procore user to set as Submittal Manager on every row of PROCORE_IMPORT.csv. Procore requires this column for an import; the user must already be in the project Directory.

## `procoreSubmittalStatus` (type: `string`):

Optional. Status written to PROCORE_IMPORT.csv (must match a status in your Procore project, e.g. Draft or Open). Blank = Procore's default (Draft).

## `maxPages` (type: `integer`):

Only the first N pages of each PDF are read.

## `maxFileSizeMb` (type: `integer`):

Larger files are rejected (not charged).

## Actor input object example

```json
{
  "pdfUrls": [
    "https://www.storycountyiowa.gov/DocumentCenter/View/16034/2024-09-11-Project-Manual-SC-Justice-Center-Remodel?bidId="
  ],
  "includeDivision01": false,
  "includeWarranty": true,
  "sectionNumberFormat": "spaced",
  "maxPages": 2000,
  "maxFileSizeMb": 150
}
```

# Actor output Schema

## `register` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `files` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "pdfUrls": [
        "https://www.storycountyiowa.gov/DocumentCenter/View/16034/2024-09-11-Project-Manual-SC-Justice-Center-Remodel?bidId="
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("hanielazevedo/spec-book-submittal-register").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "pdfUrls": ["https://www.storycountyiowa.gov/DocumentCenter/View/16034/2024-09-11-Project-Manual-SC-Justice-Center-Remodel?bidId="] }

# Run the Actor and wait for it to finish
run = client.actor("hanielazevedo/spec-book-submittal-register").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "pdfUrls": [
    "https://www.storycountyiowa.gov/DocumentCenter/View/16034/2024-09-11-Project-Manual-SC-Justice-Center-Remodel?bidId="
  ]
}' |
apify call hanielazevedo/spec-book-submittal-register --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,hanielazevedo/spec-book-submittal-register"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/EJT7tlBC6btK7fr0J/builds/ChBfxg2PfULKAmubS/openapi.json
