Dataset Output Acceptance Gate
Pricing
from $50.00 / 1,000 run assesseds
Dataset Output Acceptance Gate
Decide whether a dataset is safe to send downstream. Check schema, coverage, duplicates, currency, geography, canaries, and a saved baseline, then return accepted, partial, or rejected.
Pricing
from $50.00 / 1,000 run assesseds
Rating
0.0
(0)
Developer
Olalekan Johnson
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 days ago
Last modified
Categories
Security
Limited permissionsShare
Decide whether a dataset is safe to send into a CRM, warehouse, report, or customer workflow. You give it a dataset from your own Apify account, or a public JSON/CSV file, plus a contract. It returns accepted, partial, or rejected, with the reasons a webhook can branch on.
It is built for automation agencies and small SaaS teams that already collect data and need a gate in front of the next step. A run that merely finishes is not treated as a complete business result.
The default input needs no token and no dataset. It checks the bundled office-supply sample against the products contract and returns an accepted verdict.
What you put in
| Input | Role |
|---|---|
| Source | Bundled sample, a dataset ID, a run ID, a public JSON/CSV URL, or pasted JSON/CSV |
| Preset | Products, jobs, places, reviews, or a custom contract |
| Contract | Identity keys, types, required-field coverage, allowed values such as currency and country, min/max business rows, canaries |
| Empty allowance | Accept zero business rows when the source is quiet, including a status row that says no_results |
| Baseline | A named key-value store, plus an optional previous dataset. Set a stable baseline key for scheduled runs |
| Fail on rejection | Mark this run failed after the report is saved, so an integration stops |
Dataset and baseline-dataset fields request read access only. The baseline store requests read and write. The Actor uses limited permissions. It does not ask for access to the rest of your account.
Run ID lookup uses the run's default dataset. If the limited-permission token cannot read that run, copy the run's default dataset ID into Dataset ID instead.
Public URLs must be http or https. Localhost, private IPs, link-local addresses, and embedded credentials are rejected.
What you get back
The default dataset is flat, so a webhook, Make, n8n, or a spreadsheet can filter it without unpacking nested objects.
The first row is the verdict:
{"recordType": "verdict","verdict": "accepted","coverageStatus": "complete","confidence": "medium","gateFailed": false,"materialChange": true,"businessRows": 16,"uniqueEntities": 16,"duplicateRatio": 0,"statusOnlyRows": 0,"dominantCurrency": "USD","dominantCountry": "US","currencyShift": false,"geographyShift": false,"coverageCollapse": false,"legitimatelyEmpty": false,"lastKnownGoodDatasetId": null,"chargedRunAssessed": 1,"chargedThousandRows": 0,"sourceRef": "bundled:samples/products.json","preset": "products","profileKey": "products:bundled-sample","reasons": "","observedAt": "2026-10-23T04:19:00Z"}
Further rows are checks (recordType: "finding") and per-field coverage (recordType: "field"). The same verdict is repeated on every row.
coverageStatus is separate from the verdict:
| Status | Meaning |
|---|---|
| complete | The whole source was read and it contains business rows |
| partial | The read stopped at the row cap, so the rest was not judged |
| empty | No business rows |
| blocked | The source could not be read (permissions, private URL, HTTP error) |
| unsupported | The body was not a JSON or CSV table |
The run key-value store holds:
OUTPUT— counts, reasons, and charged unitsACCEPTANCE_REPORT— findings, field coverage, and the contract that was usedACCEPTANCE-REPORT.html— the same profile as a page
An accepted run writes a profile to the named store acceptance-gate-baselines (or the store you name). The profile keeps coverage, currency and country mix, unique-entity count, and lastKnownGoodDatasetId. A partial verdict, a rejection, or a crash does not replace that profile. If this accepted run has no dataset ID, the previous last-known-good dataset ID stays in the profile.
Confidence is medium on the first accepted run because there is no baseline yet. Later accepted runs that match a baseline can be high.
What the contract catches
- Present values with the wrong type, and review ratings outside 0–5
- Required fields below their coverage threshold
- A critical field whose coverage falls below 80% of the baseline (coverage that was already under 50% is ignored)
- Duplicate identities. Any duplicates make the verdict partial. Above the duplicate cap (default 2%) rejects
- Currency or country values outside an allow-list
- A new currency or country on more than 5% of rows, compared with the baseline
- A dominant currency or country change inside the values the baseline already saw (partial, not rejected)
- A dataset of status or error objects and no business rows
- A quiet source, when you turn on Allow a legitimately empty result
- Canary records that must appear, matched on the fields you list
- A large drop in unique entities, or a jump in upstream cost per unique entity, when the source run reports usage
Cost per unique entity is a warning. This Actor cannot refund the upstream run that already finished.
Min and max row counts apply to business rows, after status objects are removed. The check window defaults to 50,000 rows and cannot exceed 200,000. A window that stops early is partial, and it does not move the baseline, because the unseen rows were not judged.
Presets
| Preset | Identity, in order | Required coverage |
|---|---|---|
| Products | sku, then productId, then id | name 95%, price 90%, currency 90%, url 80%, sku 90% |
| Jobs | jobId, then id, then url | title 95%, company 90%, url 80% |
| Places | placeId, then id, then url | name 95%, address 80%, latitude and longitude 70% |
| Reviews | reviewId, then id, then url | rating 98% (and rating must be from 0 to 5), text 70% |
| Custom | Only what you type | Only what you type |
Alternate identity uses the first key that is filled on at least half of the business rows. Composite identity requires every key. Leave the override lists empty to keep the preset. Values you do set are merged on top.
The bundled sample is synthetic catalog data created for this Actor. It is not a scrape of a store. One product has no URL and one has no brand, which is still inside the products thresholds.
Pricing
Pay per event, for a check that finished and was written. A source that cannot be read is not charged. There is no per-dataset-item event.
| Event | When it is charged | Price |
|---|---|---|
| Run assessed | One completed verdict | $0.05 |
| 1,000 rows checked | One event per full 1,000 rows read. The remainder is included in the run assessment | $0.002 |
| Platform start | Once per run, per GB of memory. This Actor defaults to 512 MB | $0.005 per GB, about $0.0025 per run |
A baseline dataset is not billed as a second assessment. The platform also charges the start event in the table. Those are the rates this Actor is priced at.
Trial. 20 assessments of 400 rows. 20 × $0.05 = $1.00. No thousand-row events. Start charges about 20 × $0.0025 = $0.05. About $1.05.
A production estate. 300 assessments of 50,000 rows. 300 × $0.05 = $15. 300 × 50 × $0.002 = $30. Start charges about $0.75. About $45.75.
A capped run. One 50,000-row check is $0.05 + $0.10 + about $0.0025, roughly $0.15, if the row cap stays at 50,000. If the task's maximum charge is lower, charging stops when the platform budget is exhausted, chargeLimitReached is true, and unbilled thousand-row units are not charged. The data verdict is still returned. Raising the check window to the 200,000-row maximum is $0.05 + $0.40, plus the start charge, for a single assessment.
Wire it into a schedule
- Let the upstream Actor finish.
- Call this Actor with that run's default dataset ID and a stable Baseline key, such as
acme-products-daily. A new run ID on every schedule is fine. The key is what keeps the profile. - Turn on Fail the run when rejected if the next step should not run.
- In the webhook, Make, or n8n branch, read the dataset and keep the row where
recordTypeisverdict. - Stop the downstream write when
gateFailedis true orverdictisrejected. Treatpartialas needs a person unless you have decided partial data is still loadable. - For a quiet alert, turn on Dataset rows only when the profile changes and filter to
materialChangetrue. The HTML and JSON reports are still saved on every run.
Sheets: download the dataset as CSV. The columns are already flat.
A rejected gate does not undo charges from the Actor that produced the dataset. Put this gate before the system that spends money on the rows, not after a customer has already been billed for them.
Limits
- Types are checked as stored. A price that arrives as text fails a number field. CSV input does parse numeric cells.
- Country and currency comparison is literal after trimming and case-folding.
UKandGBare different until you put both in the allow-list. - The baseline stores coverage and the dataset ID, not a second copy of the rows. The report names a short identity when a type check fails. It does not copy review text.
- Two runs writing the same baseline key at the same time can race. Give each workflow its own key and one schedule.
- The first accepted run creates the baseline. It cannot yet detect a currency shift.
- Memory is 512 MB, with a 1,024 MB ceiling. Very wide rows near the 200,000 cap can be tight. Lower the check window if a run runs out of memory.
- This version does not start a fallback Actor.
Run it locally
Python 3.12.
pip install -r requirements.txtpython -m src
With no INPUT.json, the Actor uses the bundled product sample. Output is under storage/: the dataset, OUTPUT, and ACCEPTANCE-REPORT.html.
pip install pytestpytest
FAQ
Why was a successful upstream run rejected?
The upstream run can succeed and still return status objects, the wrong currency, or a field that collapsed. coverageStatus says how much was read. verdict says whether it met the contract.
A quiet day keeps failing.
Turn on Allow a legitimately empty result, or set the minimum business rows to 0. An error status is still rejected. A row that only says the source was empty (emptyReason no_results, or status empty) is accepted when the allowance is on.
Partial and rejected feel different. Rejected means a hard contract failure: do not load the data. Partial means the rows are usable with a warning, such as a few duplicates or a dominant currency that changed inside the known set. Only an accepted verdict replaces the baseline.
Will a bad run erase yesterday's good dataset ID?
No. The last accepted profile, including lastKnownGoodDatasetId, is left in place.
Does the default run call the internet?
No. It reads samples/products.json inside the Actor.
Questions: plusonecares@gmail.com.