# Bid Addendum Coverage Check (`wheresprada/bid-addendum-coverage-check`) Actor

Compare a bid amendment register with acknowledgment records. Flag missing acknowledgments, wrong solicitations, document-hash differences and deadline conflicts. A traceable pre-submission review, not legal certification or a tender scraper.

- **URL**: https://apify.com/wheresprada/bid-addendum-coverage-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.27 / completed bid addendum coverage review

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

## Bid Addendum Coverage Check

Find gaps between a published amendment register and your bid acknowledgment records before submission. This Actor flags missing acknowledgments, references to a different solicitation, fingerprints that do not match the current document, and contradictory deadline instructions. The result is a traceable review list your bid team can resolve in its existing workflow.

It is an independent record check, not a tender discovery scraper, bid submission service or legal certification. You supply the register and acknowledgment references; the Actor does not log into a procurement portal, extract PDFs or independently authenticate documents.

### Who is this for?

Construction estimators, public-sector bid teams and proposal managers who keep amendment registers in spreadsheets or an existing bid-management system. It is particularly useful when several people collect documents and prepare the final acknowledgment schedule. Automation developers can use the JSON report as a preflight step before a human reviews a bid package.

Teams seeking a complete bid desk, automatic portal monitoring or document interpretation need a broader system. This Actor complements that system by checking structured records consistently.

### Key features

- Compares each explicitly required amendment with the acknowledgment records provided.
- Requires the same solicitation, amendment reference and document SHA-256 for coverage.
- Flags acknowledgments recorded before publication or after the declared deadline.
- Distinguishes a genuinely missing acknowledgment from a supplied record that references the wrong document.
- Follows the latest supplied deadline instruction without wrongly treating older deadline extensions as current conflicts.
- Holds contradictory latest deadline instructions, incomplete registers and stale observations for review.
- Counts identical duplicate records explicitly; contradictory uses of the same ID fail instead of being silently overwritten.
- Returns references, record paths and clear reasons, without pretending to decide legal materiality.

#### Practical uses

Use it before an estimator finalizes an acknowledgment schedule, when handing a bid package from estimating to proposal operations, or when reviewing a register exported from a bid-management system. A procurement consultant can produce a consistent second-check report for each client package. It can also identify which acknowledgment records need replacement after a document revision changes its fingerprint.

### Quick start

1. Check the current official amendment register for one solicitation.
2. Enter the solicitation reference and current bid deadline, accurately converted to UTC.
3. Enter when the register was observed and whether you have its complete current contents.
4. Add each amendment's publication time, document fingerprint, official source reference and explicit acknowledgment requirement.
5. Add your acknowledgment references. The fingerprint is for the amendment document acknowledged, not for the acknowledgment form itself.
6. Run the Actor and resolve the reported gaps against the official documents.

The prefilled example is invented data with one deliberately missing acknowledgment. Its historical `asOf` date represents that demonstration snapshot. Replace it and the observation time for a current review. A completed demonstration is billable.

### Input example

```json
{
  "solicitationId": "IFB-DEMO",
  "bidDeadline": "2026-10-15T18:00:00Z",
  "asOf": "2026-10-14T16:00:00Z",
  "registerComplete": true,
  "registerObservedAt": "2026-10-14T15:00:00Z",
  "amendments": [
    {
      "id": "A-1",
      "publishedAt": "2026-10-12T12:00:00Z",
      "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "sourceUrl": "https://example.org/addendum-1.pdf",
      "requiresAcknowledgment": true
    },
    {
      "id": "A-2",
      "publishedAt": "2026-10-13T12:00:00Z",
      "documentSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
      "sourceUrl": "https://example.org/addendum-2.pdf",
      "requiresAcknowledgment": true
    }
  ],
  "acknowledgments": [
    {
      "id": "ACK-1",
      "solicitationId": "IFB-DEMO",
      "amendmentId": "A-1",
      "acknowledgedAt": "2026-10-13T12:00:00Z",
      "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "evidenceUrl": "https://example.org/ack-1.pdf"
    }
  ]
}
```

The `example.org` URLs and repeated-letter hashes are illustrative references, not real bid evidence. This exact input has been tested against the implementation.

### Input parameters

| Field | Required | Meaning |
|---|---|---|
| `solicitationId` | Yes | Reference for exactly one solicitation. IDs are normalized to uppercase. |
| `bidDeadline` | Yes | Current declared bid deadline, UTC `YYYY-MM-DDTHH:mm:ssZ`. |
| `asOf` | Yes | Review snapshot time in the same UTC format. No future evidence relative to this time is accepted. |
| `registerObservedAt` | Yes | When the published register was checked. More than 24 hours before `asOf` triggers a freshness hold. |
| `registerComplete` | Yes | Explicit declaration of complete register coverage; never independently verified. |
| `amendments` | Yes | At most 500 entries with the fields below. An empty list is supported but does not prove no amendments exist. |
| `acknowledgments` | Yes | At most 1,000 entries with the fields below. |

Each amendment requires `id`, `publishedAt`, `documentSha256`, `sourceUrl` and boolean `requiresAcknowledgment`. Set that boolean from the actual solicitation instructions; the Actor does not infer legal requirements. Optional `bidDeadline` belongs only on an amendment that changes or confirms the deadline. Do not populate old deadlines on amendments that say nothing about timing.

Each acknowledgment requires `id`, `solicitationId`, `amendmentId`, `acknowledgedAt`, `documentSha256` and `evidenceUrl`. The timestamp records the acknowledgment you are relying on. It does not establish that the government received or accepted it.

IDs are 1–80 characters using letters, numbers, periods, underscores or hyphens. Fingerprints are full 64-character SHA-256 hexadecimal strings. Source references must be HTTPS URLs without credentials, query strings or fragments. If your portal uses a signed or query-based URL, use its stable public document reference where available; never paste a session token. References are retained but not fetched.

To obtain a fingerprint locally, Node.js supports `createHash('sha256').update(documentBytes).digest('hex')`. Hash the exact amendment file version the acknowledgment refers to. A hash confirms byte equality only; it does not authenticate who issued the file.

### Output: verified demonstration

Selected fields from the complete report for the tested input:

```json
{
  "solicitationId": "IFB-DEMO",
  "summary": {
    "amendmentCount": 2,
    "acknowledgmentCount": 1,
    "identicalDuplicatesIgnored": 0,
    "requiredAcknowledgments": 2,
    "coveredRequiredAcknowledgments": 1,
    "missingRequiredAcknowledgments": 1,
    "findingCount": 1
  },
  "reviewRequired": true,
  "holds": ["Supplied-record findings require human review."],
  "findings": [
    {
      "code": "MISSING_VALID_ACKNOWLEDGMENT",
      "amendmentId": "A-2",
      "path": "amendments[1]",
      "detail": "No matching, timely, same-document acknowledgment is established by the supplied records."
    }
  ]
}
```

The full report also contains the check timestamp, declared review/deadline times, normalized amendments, reviewed acknowledgments, coverage declaration, input fingerprint and scope.

| Output | Meaning |
|---|---|
| `summary` | Unique record counts and required coverage totals. |
| `findings` | Reasons and source-array paths requiring attention. |
| `acknowledgments[].validForCoverage` | Whether that supplied record satisfies the mechanical matching rules. Not proof of legal acknowledgment. |
| `holds` | Register completeness/freshness, timing or finding conditions preventing a clean review. |
| `coverage.independentlyVerified` | Always false: the Actor does not retrieve the authoritative register. |
| `inputSha256` | Fingerprint of the submitted review input for repeatability. |

### How the review works

The Actor validates the input before producing a report. It builds a unique amendment register, then compares acknowledgment references with that register and the declared deadline. Exact duplicate records are counted and ignored; conflicting records under the same ID fail. Required amendments with no mechanically valid acknowledgment become findings.

For deadline instructions, it selects the latest publication time among amendments that explicitly supply a deadline. Two different instructions at that latest time are unresolved. A latest instruction that disagrees with the declared deadline triggers a hold. This does not decide which source legally governs; your reviewer must establish that.

No natural-language or PDF interpretation takes place. Publication times, requirement flags, references and fingerprints come from your supplied records. A clean mechanical review is only as complete and accurate as those records.

### Pricing

The proposed price is **$0.27 per completed bid addendum coverage report**, including up to 500 amendments and 1,000 acknowledgment records. One completed report is one dataset item and one `bid-addendum-review` billing event. Findings and explicit review holds are completed work and are billable. Invalid input rejected before report creation does not trigger the report event.

Actor event examples: one report $0.27; ten reports $2.70; 100 reports $27.00. These examples are not a subscription or an assurance of bid success. Applicable Apify platform charges, storage and customer-selected retention are separate; consult the Store pricing panel and your account plan. There is no percentage-of-bid charge, per-finding charge or automatic resubmission.

### API and recurring use

Your export workflow can call the Actor before a human pre-submission review. Use the official client and keep credentials in a secure environment:

```javascript
import { ApifyClient } from 'apify-client';
import fs from 'node:fs';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const input = JSON.parse(fs.readFileSync('sample_input.json', 'utf8'));
const run = await client.actor('wheresprada/bid-addendum-coverage-check').call(input);
const { value } = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('OUTPUT');
console.log(value.summary, value.holds);
```

Install `apify-client` in your integration and save the tested example as `sample_input.json`. For production, populate it with accurate current records. Never embed tokens in code, URLs or report fields.

Scheduled runs require updated input: repeatedly reviewing an old snapshot does not discover new addenda. An upstream authorized process or team member must refresh the register and observation time. Apify completion webhooks can route the resulting report to your review workflow. This Actor itself sends no emails and submits no bids.

### Limits and accuracy

Maximum input is 2,000,000 UTF-8 bytes, 500 amendments and 1,000 acknowledgment records. Runtime is bounded at 30 seconds and 128 MB. Oversized inputs fail rather than return silently incomplete results.

The 24-hour observation threshold is a conservative workflow flag, not a procurement rule or a guarantee that a recent register is complete. `asOf` is caller-supplied, so the report represents that snapshot; it cannot prove real-time freshness. Time-zone conversion, legal acknowledgment methods, materiality, waivers and receipt by the procuring authority require human review. No conclusion is made about bid responsiveness, eligibility, award probability or financial savings.

### Privacy and security

Processing requires no external network access, proxies or third-party API credentials. Do not upload personal signatures, contact details, proprietary bid prices, account tokens or credentials. URLs are not retrieved, but supplied references are included in the output and input remains in Apify storage under its applicable retention/access settings. A private reference can still reveal information even when not fetched; use suitable access controls.

Reports include the supplied aliases, fingerprints and references. Treat their dataset links as confidential. No automatic deletion, certification or independent document authentication is promised.

### Troubleshooting and FAQ

**Why is an acknowledgment missing when it is in the list?** Inspect its reasons. A wrong solicitation, different document hash, pre-publication timestamp or late timestamp prevents mechanical coverage.

**Can the Actor read my PDFs?** No. Provide structured register and acknowledgment references. Existing bid systems can map their exports into the documented shape.

**Does a matching hash prove the document is official?** No. It establishes equality with the fingerprint supplied for that amendment, not authenticity or legal effect.

**Why is the deadline held?** The latest supplied instruction disagrees with your declared deadline, or two equally recent instructions conflict. Resolve that against the official notice.

**Are all addenda legally required to be acknowledged?** The Actor does not decide. Supply the requirement from the actual bid instructions and seek appropriate advice where uncertain.

**Can I combine projects?** No. One solicitation per report prevents accidental cross-project coverage. Acknowledgments referring to another solicitation are findings.

**Is a report with holds charged?** Yes. The completed report identifies missing coverage or uncertainty. Invalid input without a completed report does not trigger the event.

**Can I treat no findings as ready to submit?** No. Review the full report, completeness declaration and official requirements. The Actor does not independently inspect the bid package or submit it.

### Support and references

Use this Actor's Apify issue tracker with a run ID and a redacted minimal reproduction. Never share tokens, signatures or confidential bid documents in a public issue.

Relevant official context: [FAR Part 14, amendments and minor informalities](https://www.acquisition.gov/far/part-14). Rules vary by solicitation and jurisdiction; this reference explains the workflow's importance rather than providing a universal decision rule. API and scheduling instructions are in the [Apify documentation](https://docs.apify.com/platform).

# Actor input Schema

## `solicitationId` (type: `string`):

The exact solicitation reference. Short alias, no contact details.

## `bidDeadline` (type: `string`):

Current declared submission deadline, converted accurately to UTC: YYYY-MM-DDTHH:mm:ssZ.

## `asOf` (type: `string`):

Time represented by this review, UTC. For a current check, replace the historical demonstration date.

## `registerComplete` (type: `boolean`):

True only if you checked the complete current published register. A declaration, not independent proof.

## `registerObservedAt` (type: `string`):

When you last checked the published amendment register, UTC. More than 24 hours before asOf triggers a freshness hold.

## `amendments` (type: `array`):

Up to 500. Each: id, publishedAt, documentSha256, sourceUrl, requiresAcknowledgment. Optional bidDeadline only when that amendment explicitly changes or confirms the deadline. URLs are reference-only; no fetching.

## `acknowledgments` (type: `array`):

Up to 1000. Each: id, solicitationId, amendmentId, acknowledgedAt, documentSha256, evidenceUrl. Fingerprint the actual amendment acknowledged, not the acknowledgment form. No personal signatures or credentials.

## Actor input object example

```json
{
  "solicitationId": "IFB-DEMO",
  "bidDeadline": "2026-10-15T18:00:00Z",
  "asOf": "2026-10-14T16:00:00Z",
  "registerComplete": true,
  "registerObservedAt": "2026-10-14T15:00:00Z",
  "amendments": [
    {
      "id": "A-1",
      "publishedAt": "2026-10-12T12:00:00Z",
      "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "sourceUrl": "https://example.org/addendum-1.pdf",
      "requiresAcknowledgment": true
    },
    {
      "id": "A-2",
      "publishedAt": "2026-10-13T12:00:00Z",
      "documentSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
      "sourceUrl": "https://example.org/addendum-2.pdf",
      "requiresAcknowledgment": true
    }
  ],
  "acknowledgments": [
    {
      "id": "ACK-1",
      "solicitationId": "IFB-DEMO",
      "amendmentId": "A-1",
      "acknowledgedAt": "2026-10-13T12:00:00Z",
      "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "evidenceUrl": "https://example.org/ack-1.pdf"
    }
  ]
}
```

# Actor output Schema

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

No description

## `reviews` (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 = {
    "solicitationId": "IFB-DEMO",
    "bidDeadline": "2026-10-15T18:00:00Z",
    "asOf": "2026-10-14T16:00:00Z",
    "registerComplete": true,
    "registerObservedAt": "2026-10-14T15:00:00Z",
    "amendments": [
        {
            "id": "A-1",
            "publishedAt": "2026-10-12T12:00:00Z",
            "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "sourceUrl": "https://example.org/addendum-1.pdf",
            "requiresAcknowledgment": true
        },
        {
            "id": "A-2",
            "publishedAt": "2026-10-13T12:00:00Z",
            "documentSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
            "sourceUrl": "https://example.org/addendum-2.pdf",
            "requiresAcknowledgment": true
        }
    ],
    "acknowledgments": [
        {
            "id": "ACK-1",
            "solicitationId": "IFB-DEMO",
            "amendmentId": "A-1",
            "acknowledgedAt": "2026-10-13T12:00:00Z",
            "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "evidenceUrl": "https://example.org/ack-1.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("wheresprada/bid-addendum-coverage-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 = {
    "solicitationId": "IFB-DEMO",
    "bidDeadline": "2026-10-15T18:00:00Z",
    "asOf": "2026-10-14T16:00:00Z",
    "registerComplete": True,
    "registerObservedAt": "2026-10-14T15:00:00Z",
    "amendments": [
        {
            "id": "A-1",
            "publishedAt": "2026-10-12T12:00:00Z",
            "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "sourceUrl": "https://example.org/addendum-1.pdf",
            "requiresAcknowledgment": True,
        },
        {
            "id": "A-2",
            "publishedAt": "2026-10-13T12:00:00Z",
            "documentSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
            "sourceUrl": "https://example.org/addendum-2.pdf",
            "requiresAcknowledgment": True,
        },
    ],
    "acknowledgments": [{
            "id": "ACK-1",
            "solicitationId": "IFB-DEMO",
            "amendmentId": "A-1",
            "acknowledgedAt": "2026-10-13T12:00:00Z",
            "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "evidenceUrl": "https://example.org/ack-1.pdf",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("wheresprada/bid-addendum-coverage-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 '{
  "solicitationId": "IFB-DEMO",
  "bidDeadline": "2026-10-15T18:00:00Z",
  "asOf": "2026-10-14T16:00:00Z",
  "registerComplete": true,
  "registerObservedAt": "2026-10-14T15:00:00Z",
  "amendments": [
    {
      "id": "A-1",
      "publishedAt": "2026-10-12T12:00:00Z",
      "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "sourceUrl": "https://example.org/addendum-1.pdf",
      "requiresAcknowledgment": true
    },
    {
      "id": "A-2",
      "publishedAt": "2026-10-13T12:00:00Z",
      "documentSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
      "sourceUrl": "https://example.org/addendum-2.pdf",
      "requiresAcknowledgment": true
    }
  ],
  "acknowledgments": [
    {
      "id": "ACK-1",
      "solicitationId": "IFB-DEMO",
      "amendmentId": "A-1",
      "acknowledgedAt": "2026-10-13T12:00:00Z",
      "documentSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "evidenceUrl": "https://example.org/ack-1.pdf"
    }
  ]
}' |
apify call wheresprada/bid-addendum-coverage-check --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,wheresprada/bid-addendum-coverage-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/evnx5HitnZujdAphx/builds/YJ8EJoadYWWTZecGN/openapi.json
