# A product feed OpenAI accepts, validated line by line

> OpenAI drops malformed feed rows without telling you. Convrail syncs your catalog, checks every product against the official specification (field lengths, price format, image files, identifiers, placeholder values), exports the accepted rows in the format OpenAI expects and delivers them by SFTP. Rejected rows are listed with the reason and the fix.

Canonical: https://convrail.com/product-feed/

## The silent rejection problem

The OpenAI feed ingestion does not return an error report per row. A title in capital letters, HTML left in a description or a price written as "25,99" means the product is skipped, and nothing tells you. Merchants discover it weeks later when a product they sell every day never appears in ChatGPT. Convrail moves that check to your side of the fence: every row is validated before delivery, and every rejection is written to the run journal with the field, the rule and the correction.

- Without: "25,99" as a price: the row is gone and so is the product
- With: caught before delivery: price must be "25.99 EUR"

## The 9 required fields, with the exact limits

These are the rules Convrail enforces, taken from the OpenAI product feed specification. Each one is covered by an automated test.

*Required fields of the OpenAI product feed and how Convrail validates them*

| Field | Rule | Typical rejection |
| --- | --- | --- |
| item_id | Required, stable, 100 characters max | Variant ID that changes on re-import |
| title | Required, 150 characters max, not all capital letters | "WIRELESS HEADPHONES XL" |
| description | Required, 5,000 characters max, plain text | HTML tags left from the store editor |
| url | Required, valid http(s) URL | Relative path |
| brand | Required, 70 characters max | Empty vendor field |
| image_url | Required, direct HTTP(S) link to a JPEG or PNG, HTTPS recommended | .webp file, URL with credentials |
| price | "<amount> <ISO-4217>", greater than 0 | "25,99", "25.99", "€25.99" |
| availability | in_stock, out_of_stock, pre_order, backorder or unknown | "available" |
| seller_name | Required, real seller name, 70 characters max | Missing, "unknown" |

Conditional and recommended fields are checked the same way when they are present, because a malformed optional field also drops the row.

*Conditional and recommended fields validated by Convrail*

| Field | Rule | Typical rejection |
| --- | --- | --- |
| is_eligible_search | Lowercase "true" or "false"; omitted means true | Boolean, "TRUE", "yes" |
| is_eligible_checkout | When "true": search eligibility plus seller_privacy_policy and seller_tos URLs | Checkout enabled without policy URLs |
| is_ads_eligible | Lowercase "true" or "false"; required for ads processing | "1", "yes" |
| target_countries | Uppercase ISO 3166-1 alpha-2 codes, configured with your OpenAI setup | "France", "USA" |
| gtin | 8, 12, 13 or 14 digits with a valid check digit | Typo in the barcode, dashes |
| sale_price | Same currency, strictly less than price | Sale price equal to price |
| group_id | Stable and different from item_id | group_id copied from item_id |
| brand, seller_name | Real values, never placeholders | "n/a", "null", "unknown" |

Also checked when present: `mpn` (70 characters max), `availability_date` (ISO 8601), `star_rating` and `popularity_score` (0 to 5), `review_count` and `return_deadline_in_days` (positive integers). When neither GTIN nor MPN exists, `identifier_exists` is set to "no". A GTIN with a wrong check digit is omitted rather than sent, so the row stays valid.

## From catalog to delivered file

1. **Your catalog stays in sync** Product webhooks keep Convrail’s copy of your catalog current. Changes are debounced for 30 seconds so a bulk edit triggers one rebuild, not a hundred. A full sync is available on demand.
2. **Each product becomes a feed item** HTML is stripped from descriptions, prices are formatted with the store currency, variants are grouped under `group_id`, and your seller name, target countries and eligibility flags are applied from the feed settings.
3. **Every row is validated** Accepted rows go into the file. Rejected rows are journaled with the field and the rule they broke. The count of rejections is visible in the app before the file leaves.
4. **The file is exported and sharded** Formats: `parquet`, `jsonl.gz`, `csv.gz` or `tsv.gz`. Files are split at 500,000 items or roughly 450 MB, under the limits of the specification, with stable names (`feed-organic-000.jsonl.gz`) so each delivery overwrites the previous snapshot.
5. **Delivery by SFTP on your schedule** Convrail connects to the SFTP endpoint from your OpenAI account at the hour you pick and uploads the shards. Failed deliveries retry, then open a health alert.

## A journal for every run

Each run records the number of items read, accepted and rejected, the diff against the previous run (added, removed, changed) and the delivery status. The per-row errors are kept with the run, so you can trace when a product disappeared from the feed and why.


| Run field | Meaning |
| --- | --- |
| itemsTotal, itemsValid, itemsRejected | How many rows were built, kept and dropped |
| added, removed, changed | Diff against the previous run, computed from a hash of each row |
| errors | One entry per rejected item: item_id, field, message |
| deliveryStatus | pending, delivered or failed, with the attempt count |

## If you already have a Google Shopping feed

Most fields transfer, some do not. OpenAI expects `is_eligible_search`, `is_eligible_checkout` and `target_countries` that Google never asked for, uses `seller_name` instead of a merchant account, and formats booleans as lowercase strings. Convrail keeps a Google-compatible projection of your feed as well, so the same catalog serves both channels without maintaining two exports.

## Frequently asked questions

### Which formats does OpenAI accept for the product feed?

Parquet (recommended for large catalogs), gzipped JSON Lines, CSV and TSV. Convrail exports all four; parquet and jsonl.gz are the defaults.

### How often is the feed delivered?

Once a day, at the hour you choose, as a complete snapshot with stable filenames so each delivery replaces the previous one. Intraday updates through the API are on the roadmap.

### What happens to a product with an invalid row?

It is excluded from that delivery and listed in the run journal with the field and the rule it broke. Fix the product in your store and the next run picks it up.

### Do I need a separate feed for ChatGPT Ads?

The specification allows the same items to serve organic results and ads through the `is_ads_eligible` flag. Convrail sets it from your feed settings; a distinct ads delivery target is planned.

### How large can my catalog be?

Files are sharded at 500,000 items or about 450 MB each, within the limits of the specification. In our automated test, a 10,000-product catalog is validated, exported and delivered to SFTP in under a minute.

### Where do my SFTP credentials live?

Encrypted at rest with AES-256-GCM, like every secret Convrail stores. They are decrypted only at delivery time.
