My Actor avatar

My Actor

Pricing

Pay per usage

Go to Apify Store
My Actor

My Actor

Pricing

Pay per usage

Rating

0.0

(0)

Developer

NICHOLAS SAVAGE

NICHOLAS SAVAGE

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 days ago

Last modified

Categories

Share

AgentShelf — OpenAI product feed conformance validator

Give it your product feed. It tells you, row by row, which items OpenAI's agentic-commerce ingest would reject and exactly why — before you ever upload anything.

It runs entirely offline. It does not upload your feed, it does not call an OpenAI API, and it does not need a merchant account. It reads bytes you hand it and applies the rules from the published feed specification.

What it does

You give itYou get back
a CSV, TSV, JSON or NDJSON feeda conformance report: accepted vs. rejected counts, an acceptance rate, duplicate item ids, unknown and missing columns
the same, normalize: truethe above plus every accepted row projected into OpenAI native field names, exported as a CSV you can ship

Both dialects are understood. Point it at an existing Google Merchant Center feed and it maps the Google field names onto the OpenAI ones before checking, so you can find out what an existing catalogue would cost you without rebuilding it first.

Every finding carries a rule id, the field it fired on, the offending value and the source line number, so a rejection is something you can fix rather than something you have to guess at.

Set failOn to decide which findings make the run exit non-zero. The report is stored either way, so a "failed" run is still a readable report.

Input

Full contract in input_schema.json. Unknown fields are rejected, not ignored — including anything that looks like a URL, because this Actor performs no network requests of its own. Supply the feed exactly once, either inline as feedText or as a record in this run's own key-value store named by feedKey.

FieldTypeDefaultNotes
feedTextstringthe feed itself, inline
feedKeystringa record name in this run's own key-value store holding the feed; a plain name, not a path
formatauto | csv | tsv | json | ndjsonautoforce a parser instead of sniffing the bytes
dialectauto | openai | googleautowhich field-name and rule set to apply
merchantNamestringyour registered display name, checked against the seller_name rule; taken on trust, not a credential
adsFeedbooleanfalsealso apply the rules that only bind for Ads feeds
checkRecommendedbooleantruealso report recommended-field gaps as warnings
normalizebooleanfalsemap to native field names and emit the accepted rows
emitCsvbooleantruewrite the CSV exports as key-value records
failOnerror | warning | nevererrorwhich findings make the run exit 1
maxRowsinteger50000refuse a larger feed rather than validate a prefix of it (ceiling 500000)
tenantIdstringfree-text label echoed into the result for your own attribution

Output

One agentshelf/actor-result/1 object per run, written identically to the default dataset and to the OUTPUT key-value record, on success and on failure alike. The shape is described in dataset_schema.json and output_schema.json; a real local run's output is in ./sample-output.json, produced by

npm ci && npm run build
mkdir -p storage/key_value_stores/default
# the bundled Google-dialect fixture as the `feedText` field
node products/agentshelf/dist/actor/main.js

so you can reproduce it apart from the timestamp and the duration.

The CSV exports are not inlined into that JSON. They are stored as their own key-value records, so you can download them as spreadsheets:

RecordWritten when
rejections.csva report was produced and emitCsv was left on — one row per finding
accepted-rows.csvnormalize and emitCsv were both on — the passing rows in native fields

A very large feed can produce a per-row array too big for one dataset item. In that case the summary, the rule tally and the digest are kept, the per-row array is set to null, and data.truncated says how many rows were dropped. The full per-row detail is still in rejections.csv. Nothing is silently trimmed.

Exit codes:

CodeMeaning
0ran, report stored, failOn policy not tripped
1findings at or above the failOn threshold (the report is still stored)
2input missing, invalid, oversized, or containing an unknown field
3runtime or storage failure

Pricing

Pay per event, two events, charged once per run:

EventPriceCharged when
feed-normalization$0.10once, after a report and the normalized native rows have been written to both the dataset and OUTPUT
feed-report$0.05once, after a report alone has been written to both the dataset and OUTPUT

Pricing is per run, not per row: a 200-row feed and a 200,000-row feed cost the same. You can price a validation into your own pipeline without knowing the catalogue size in advance.

Nothing else is billable. In particular you are not charged for:

  • a rejected, invalid or oversized input (exit 2),
  • a runtime failure (exit 3),
  • a run whose failOn gate tripped (exit 1) — findings are news you asked for, and that run is a failed run,
  • a run whose result could not be stored,
  • a start, a retry or a resurrect.

The charge is issued once per run, with an idempotency key, only after the result is durably stored, and the run's exit code never depends on whether the billing call succeeded. Your maximum cost per run is respected before any charge is sent.

Those two prices are what this repository's code, tests and ledger agree on. Whether they are what a published listing charges depends on the Apify Console configuration, which is not part of this repository.

Security

  • No network egress. The run performs no outbound requests except to the Apify platform API that stores the result and issues the charge. There is no input field naming a URL, host or proxy, and unknown fields are rejected, so there is no user-influenced destination and no SSRF surface. feedKey is validated as a plain record name, so it cannot be pointed at a path.
  • No credentials. No input field is a secret, because the Actor has nothing to authenticate to. merchantName is a public display name you choose to supply and is echoed back deliberately. The platform token the run receives is used only for storage and charging, and never appears in the dataset, the OUTPUT record, a CSV export or a log line.
  • Your feed is not retained. It is read, validated and discarded with the run's own storage. The result carries a digest of the feed, never the feed.
  • Row counts, feed size and result size are all capped, and the run executes as an unprivileged user in the container.

Boundaries

  • Not an upload path. It transmits nothing to OpenAI and holds no merchant account. Passing here is not submitting.
  • Not the ingest authority. A clean report reduces rejection risk. It is not a guarantee of acceptance: OpenAI is the authority on its own ingest, and the specification can change ahead of this rule set.
  • Operator-side configuration cannot be checked. Your registered merchant name, your configured market currencies and your Ads settings live in an account this tool cannot see. What it assumed is reported in every result as assumptions rather than presented as verified.
  • Links are checked for syntax only. It does not fetch product or image URLs, so "must resolve with HTTP 200" is not something it can confirm.
  • Not affiliated with or endorsed by OpenAI or Google.

Licence

Apache-2.0.