# Construction Payment Application Checker (`wheresprada/construction-pay-application-check`) Actor

Check construction pay-application totals against schedule-of-values lines, retainage, approved changes and prior certificates. Get exact arithmetic differences with input references. No OCR, ERP login or payment certification.

- **URL**: https://apify.com/wheresprada/construction-pay-application-check.md
- **Developed by:** [Mahruf Hossain](https://apify.com/wheresprada) (community)
- **Categories:** Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.20 / completed payment application check

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

## Construction Payment Application Checker

Check the arithmetic in a construction progress-billing application before another review cycle begins. This Actor compares your cover-sheet totals with schedule-of-values lines, reported retainage, optional approved-change totals and an optional previous certificate. It returns exact differences and the input locations behind them, ready for a reviewer or automation workflow.

This is an independent check of existing numbers, not a billing system or a form generator. It does not determine whether work was performed, whether a change was approved, or whether payment is legally due. You remain responsible for those decisions.

### Key features

- Recalculate completed-and-stored amounts and balances for each schedule line.
- Compare the schedule total with the original contract plus net changes.
- Reconcile line-level retainage with the cover-sheet retainage total.
- Check earned-less-retainage, previous certificates, current payment due and balance to finish.
- Optionally compare a supplied list of approved change amounts with the cover-sheet net change total.
- Optionally compare an independently supplied previous certified amount with the current application's previous-certificate field.
- Receive decimal-string results without binary floating-point rounding and with a stable input fingerprint.
- Distinguish evidence that was checked from evidence that was not supplied.

Each completed run returns one report. A report with discrepancies is successful work, not an application failure. Malformed input is rejected rather than silently skipped.

### Who should use this Actor?

Construction controllers, project accountants, owner representatives and contractor-focused bookkeepers can use it as a repeatable arithmetic review step. It is especially relevant when numbers move between spreadsheets, billing portals and internal review tools.

Use it when you already have structured billing numbers. It does not read PDFs or images. If your process depends on extracting figures from scanned documents, verify that extraction separately before running this checker. A perfectly consistent report can still contain incorrect source data.

### Real-world use cases

1. Check a monthly subcontractor application before assembling the owner's billing package.
2. Recheck a revised application and compare the findings with the previous run.
3. Add a review step after exporting schedule-of-values figures from a spreadsheet.
4. Give an accounting advisor a consistent exception report alongside the original project records.
5. Flag cover-sheet amounts that no longer agree with a revised continuation schedule.

These are intended workflows, not claims of customer adoption or guaranteed payment acceleration.

### Quick start

Open the Input tab and use the prefilled example. It contains two illustrative schedule lines and a deliberately overstated current payment request. Run once, then open **Complete arithmetic report** in Output. The calculated current due is `17800.00`; the example reports `18000.00`, so the report flags a `200.00` difference.

Replace the example with your own figures only after confirming field meanings. Use decimal strings such as `"1250.00"`, not JSON numbers, formatted currency or scientific notation. Use a single currency throughout the application. No exchange-rate conversion occurs.

#### Example input

```json
{
  "currency": "USD",
  "cover": {
    "originalContract": "100000.00",
    "netChanges": "0.00",
    "contractToDate": "100000.00",
    "completedStored": "40000.00",
    "retainage": "4000.00",
    "earnedLessRetainage": "36000.00",
    "previousCertificates": "20000.00",
    "currentDue": "16000.00",
    "balanceToFinish": "64000.00"
  },
  "lines": [{
    "id": "MEP-01",
    "scheduled": "100000.00",
    "previousWork": "25000.00",
    "currentWork": "10000.00",
    "stored": "5000.00",
    "completedStored": "40000.00",
    "retainage": "4000.00",
    "balance": "60000.00"
  }],
  "approvedChanges": [],
  "previousCertified": "20000.00"
}
```

### Input parameters

| Field | Required | Meaning and limits |
|---|---|---|
| `currency` | Yes | Three uppercase letters; one currency per application. |
| `cover` | Yes | All nine amounts shown in the example; decimal strings. |
| `lines` | Yes | Between 1 and 5,000 schedule items with unique IDs. |
| `approvedChanges` | No | Up to 5,000 unique `{id, amount}` objects representing already-approved changes. |
| `previousCertified` | No | Independent cumulative certificate amount for comparison. |

Every schedule line requires the eight fields shown above. `previousWork` means completed work carried from prior applications, `currentWork` means this period's completed work, and `stored` means materials presently stored. Do not include the same materials in both current work and stored materials. `retainage` is a reported monetary amount, not a percentage. Its contractual correctness is outside this tool's scope.

All monetary strings allow at most twelve integer digits and two decimal places. Negative amounts are accepted for legitimate adjustments; review their meaning yourself. Identifiers allow 1–120 characters without control characters. Unknown fields, duplicate identifiers, missing amounts and requests larger than two million UTF-8 bytes are rejected. Optional evidence omitted is marked `not_supplied`; an empty change list asserts zero approved changes.

### Output and findings

The default dataset contains one complete report, also saved as `OUTPUT` in the run's key-value store. `summary` contains recalculated totals; `findings` contains discrepancy codes, input paths, reported amounts, expected amounts and differences. `lines` preserves IDs and zero-based input indexes with calculated values. `coverage` describes optional comparisons and exclusions. `inputSha256` identifies the exact input JSON serialization; it does not authenticate underlying documents.

A representative finding for the prefilled example is:

```json
{
  "code": "COVER_CURRENT_DUE_MISMATCH",
  "path": "cover.currentDue",
  "actual": "18000.00",
  "expected": "17800.00",
  "difference": "200.00"
}
```

Positive differences mean the reported amount exceeds the calculated comparator. They are not automatically overpayments or recoverable revenue. A zero-finding report means the implemented checks found no differences in the supplied numbers, not that the payment application is complete or certified.

### How the checks work

Calculations use integer cents. Completed-and-stored value is previous work plus current work plus presently stored materials. Line balance is scheduled value minus that calculated total. Retainage is summed from reported line amounts; this tool does not infer percentages or apply statutory limits.

Cover comparisons use recalculated schedule totals, so an incorrect line subtotal does not hide a cover-sheet inconsistency. Earned less retainage is calculated completed-and-stored value minus reported line retainage. Current due subtracts reported previous certificates. Cover balance to finish includes retainage. Optional change and certificate comparisons use only the values you supply; no external database is consulted.

### Pricing and cost examples

Launch pricing is **$0.20 per completed application-check report**, covering up to 5,000 schedule lines and 5,000 supplied change entries. One report is one billing event regardless of how many findings it contains. Malformed input is rejected before the report event. There is no per-discrepancy fee and no subscription purchased by this Actor.

One report costs $0.20 in Actor fees; 100 reports cost $20; 1,000 reports cost $200. Apify may also charge applicable platform usage under your plan. These are arithmetic examples, not forecasts of adoption or earnings. Set your run charge limit to at least $0.20. If output storage or charging fails, the run fails; inspect the run and dataset before retrying. A new manual run is a new chargeable report, even for identical input. Automatic retries and container restarts are disabled in the default configuration.

### API integration and scheduling

Submit the tested JSON as the request body using a token stored in your environment:

```javascript
const response = await fetch(
  'https://api.apify.com/v2/acts/wheresprada~construction-pay-application-check/runs?memory=128&timeout=30&maxTotalChargeUsd=0.20',
  { method: 'POST', headers: {
      Authorization: `Bearer ${process.env.APIFY_TOKEN}`,
      'Content-Type': 'application/json'
    }, body: JSON.stringify(input) }
);
if (!response.ok) throw new Error(`Apify returned ${response.status}`);
const {data: run} = await response.json();
console.log(run.id);
```

Poll the run until it finishes, then retrieve its default dataset. Never interpret a successful API submission as a completed report. An Apify task can save input for repeated use; update it with the current billing period before scheduling. A schedule with unchanged input merely repeats the same review and can incur repeated fees. Use webhooks for successful completion and handle failed runs separately. This Actor does not automatically connect to your accounting system.

### Limits and accuracy

The fixed runtime is 128 MB with a 30-second timeout. There are no network fetches, proxies, OCR, AI calculations or external services. Two-decimal currencies only; currencies requiring different minor-unit precision are unsupported. Mixed-currency applications must be separated before use.

No checks establish physical progress, material ownership, lien-waiver validity, approval authenticity, contractual billing rights, retainage law or whether cash actually arrived. Previous certificates are not necessarily cash receipts. Retainage releases and unusual contract terms require professional interpretation. This is not a legal opinion, accounting audit or payment certification. AIA, G702 and G703 are marks of their respective owner; this independent tool is not affiliated with AIA and does not reproduce licensed forms.

### Privacy and security

The implementation processes supplied input without contacting external websites. It does not need credentials to an ERP, bank or billing portal. Apify stores input, logs and output under its platform storage/access rules and your account settings. Do not submit bank details, personal identifiers or confidential records without appropriate authorization. Delete retained run storage when no longer required; this Actor does not promise automatic deletion or a particular retention period.

### Troubleshooting and FAQ

**Why did a number fail validation?** Use quoted decimal strings without commas, symbols or more than two decimal places.

**Why does a blank optional change list flag a discrepancy?** An empty list means verified zero; omit the field if you do not have the evidence.

**Can I upload a PDF?** No. Transfer and verify the relevant numbers into the input structure first.

**Why does line balance differ from cover balance?** Cover balance includes retainage; line balance excludes it in this model.

**Does a flagged retainage amount prove a violation?** No. A range flag is a review prompt; contractual and legal requirements are not evaluated.

**Will retries charge again?** A new completed run is another report. Do not retry a successful report just because it contains findings.

**Can this certify a payment?** No. A qualified human must review the records and make that decision.

### Support

Open an issue on this Actor's Apify Store page with the run ID, expected result, actual finding code and a minimal anonymized input. Never post tokens, bank details or confidential customer documents. Include field names and a small reproducible example so the calculation can be investigated without exposing your project records.

# Actor input Schema

## `currency` (type: `string`):

One currency per application; uppercase three-letter code. No conversion.

## `cover` (type: `object`):

Nine decimal-string totals. Use the tested example; do not enter currency symbols.

## `lines` (type: `array`):

1–5000 rows with unique IDs and the seven documented decimal-string amounts.

## `approvedChanges` (type: `array`):

Only already-approved changes. Omit when unavailable; an empty array means confirmed zero.

## `previousCertified` (type: `string`):

Independent prior certificate total; not bank cash received.

## Actor input object example

```json
{
  "currency": "USD",
  "cover": {
    "originalContract": "100000.00",
    "netChanges": "5000.00",
    "contractToDate": "105000.00",
    "completedStored": "42000.00",
    "retainage": "4200.00",
    "earnedLessRetainage": "37800.00",
    "previousCertificates": "20000.00",
    "currentDue": "18000.00",
    "balanceToFinish": "67200.00"
  },
  "lines": [
    {
      "id": "MEP-01",
      "scheduled": "100000.00",
      "previousWork": "25000.00",
      "currentWork": "10000.00",
      "stored": "5000.00",
      "completedStored": "40000.00",
      "retainage": "4000.00",
      "balance": "60000.00"
    },
    {
      "id": "CO-01",
      "scheduled": "5000.00",
      "previousWork": "0.00",
      "currentWork": "2000.00",
      "stored": "0.00",
      "completedStored": "2000.00",
      "retainage": "200.00",
      "balance": "3000.00"
    }
  ],
  "approvedChanges": [
    {
      "id": "CO-01",
      "amount": "5000.00"
    }
  ],
  "previousCertified": "20000.00"
}
```

# Actor output Schema

## `report` (type: `string`):

No description

## `dataset` (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 = {
    "cover": {
        "originalContract": "100000.00",
        "netChanges": "5000.00",
        "contractToDate": "105000.00",
        "completedStored": "42000.00",
        "retainage": "4200.00",
        "earnedLessRetainage": "37800.00",
        "previousCertificates": "20000.00",
        "currentDue": "18000.00",
        "balanceToFinish": "67200.00"
    },
    "lines": [
        {
            "id": "MEP-01",
            "scheduled": "100000.00",
            "previousWork": "25000.00",
            "currentWork": "10000.00",
            "stored": "5000.00",
            "completedStored": "40000.00",
            "retainage": "4000.00",
            "balance": "60000.00"
        },
        {
            "id": "CO-01",
            "scheduled": "5000.00",
            "previousWork": "0.00",
            "currentWork": "2000.00",
            "stored": "0.00",
            "completedStored": "2000.00",
            "retainage": "200.00",
            "balance": "3000.00"
        }
    ],
    "approvedChanges": [
        {
            "id": "CO-01",
            "amount": "5000.00"
        }
    ],
    "previousCertified": "20000.00"
};

// Run the Actor and wait for it to finish
const run = await client.actor("wheresprada/construction-pay-application-check").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 = {
    "cover": {
        "originalContract": "100000.00",
        "netChanges": "5000.00",
        "contractToDate": "105000.00",
        "completedStored": "42000.00",
        "retainage": "4200.00",
        "earnedLessRetainage": "37800.00",
        "previousCertificates": "20000.00",
        "currentDue": "18000.00",
        "balanceToFinish": "67200.00",
    },
    "lines": [
        {
            "id": "MEP-01",
            "scheduled": "100000.00",
            "previousWork": "25000.00",
            "currentWork": "10000.00",
            "stored": "5000.00",
            "completedStored": "40000.00",
            "retainage": "4000.00",
            "balance": "60000.00",
        },
        {
            "id": "CO-01",
            "scheduled": "5000.00",
            "previousWork": "0.00",
            "currentWork": "2000.00",
            "stored": "0.00",
            "completedStored": "2000.00",
            "retainage": "200.00",
            "balance": "3000.00",
        },
    ],
    "approvedChanges": [{
            "id": "CO-01",
            "amount": "5000.00",
        }],
    "previousCertified": "20000.00",
}

# Run the Actor and wait for it to finish
run = client.actor("wheresprada/construction-pay-application-check").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 '{
  "cover": {
    "originalContract": "100000.00",
    "netChanges": "5000.00",
    "contractToDate": "105000.00",
    "completedStored": "42000.00",
    "retainage": "4200.00",
    "earnedLessRetainage": "37800.00",
    "previousCertificates": "20000.00",
    "currentDue": "18000.00",
    "balanceToFinish": "67200.00"
  },
  "lines": [
    {
      "id": "MEP-01",
      "scheduled": "100000.00",
      "previousWork": "25000.00",
      "currentWork": "10000.00",
      "stored": "5000.00",
      "completedStored": "40000.00",
      "retainage": "4000.00",
      "balance": "60000.00"
    },
    {
      "id": "CO-01",
      "scheduled": "5000.00",
      "previousWork": "0.00",
      "currentWork": "2000.00",
      "stored": "0.00",
      "completedStored": "2000.00",
      "retainage": "200.00",
      "balance": "3000.00"
    }
  ],
  "approvedChanges": [
    {
      "id": "CO-01",
      "amount": "5000.00"
    }
  ],
  "previousCertified": "20000.00"
}' |
apify call wheresprada/construction-pay-application-check --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,wheresprada/construction-pay-application-check"
        }
    }
}
```

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/T1JxUcDvlisxxBs9t/builds/wkFHI35To4lrUBmet/openapi.json
