angeo / module-openai-product-feed-api
angeo/module-openai-product-feed-api
Magento 2 REST API for OpenAI Agentic Commerce Protocol (ACP). Implements the full 6-endpoint ACP feed surface: feeds, products (with full pagination and configurable variants), and promotions. DB-persisted feeds, POST-based PATCH workaround for Magento compatibility.
Angeo OpenAI Product Feed API — Magento 2
Magento 2 REST API for the OpenAI Agentic Commerce Protocol (ACP). Full 7-endpoint feed surface with DB-persisted feeds, paginated product export, and SalesRule → Promotion mapping.
What this module fixes
- DB-persisted feeds — feed IDs survive cache flushes and server restarts (
angeo_acp_feedtable via Schema Patch) - Proper pagination —
getProductsnow iterates the full catalog in batches (v1 silently capped at 100 products) - PATCH workaround documented — Magento has no native PATCH support; upserts exposed via
/upsertPOST + nginx rewrite - Structured validation errors — upsert responses include
errors: string[]per field, not a silentfalse - Category names resolved — products now return
{value: "Tools", taxonomy: "merchant"}instead of{value: "42"} - ImageUrlBuilder — product images use Magento's proper resized URL builder, not raw file paths
/invalidateendpoint — bust product cache without a deploy- Coupon codes in promotion descriptions — auto-appended when rule has a specific coupon
- Multiple benefits per promotion — e.g. 10% off + free shipping in one
benefitsarray GET /product_feeds— list all feeds (new endpoint)
Endpoints
| Method | Path | Description |
|---|---|---|
POST |
/rest/V1/angeo/product_feeds |
Create a product feed |
GET |
/rest/V1/angeo/product_feeds |
List all feeds |
GET |
/rest/V1/angeo/product_feeds/:id |
Get feed metadata |
GET |
/rest/V1/angeo/product_feeds/:id/products |
Get products (paginated) |
POST |
/rest/V1/angeo/product_feeds/:id/products/upsert |
Upsert products (PATCH workaround) |
POST |
/rest/V1/angeo/product_feeds/:id/products/invalidate |
Bust product cache |
GET |
/rest/V1/angeo/product_feeds/:id/promotions |
Get promotions |
POST |
/rest/V1/angeo/product_feeds/:id/promotions/upsert |
Upsert promotions (PATCH workaround) |
PATCH workaround — nginx setup
Magento 2's REST framework does not support HTTP PATCH. OpenAI's ACP crawler sends PATCH /product_feeds/:id/products. Bridge this with an nginx rewrite before the request reaches Magento:
# Add inside your server {} block
location ~ ^/rest/V1/angeo/product_feeds/[^/]+/products$ {
if ($request_method = PATCH) {
rewrite ^(.*)$ $1/upsert last;
}
}
location ~ ^/rest/V1/angeo/product_feeds/[^/]+/promotions$ {
if ($request_method = PATCH) {
rewrite ^(.*)$ $1/upsert last;
}
}
Apache (.htaccess in Magento root):
RewriteCond %{REQUEST_METHOD} ^PATCH$
RewriteRule ^rest/V1/angeo/product_feeds/([^/]+)/products$ rest/V1/angeo/product_feeds/$1/products/upsert [L]
RewriteCond %{REQUEST_METHOD} ^PATCH$
RewriteRule ^rest/V1/angeo/product_feeds/([^/]+)/promotions$ rest/V1/angeo/product_feeds/$1/promotions/upsert [L]
Installation
composer require angeo/module-openai-product-feed-api
bin/magento setup:upgrade
bin/magento cache:flush
Configuration
Navigate to Stores → Configuration → Angeo → Product Feed API.
| Setting | Description | Default |
|---|---|---|
| Enabled | Enable/disable the API | Yes |
| Default Target Country | ISO 3166-1 alpha-2 | US |
| UTM Medium | Appended to all product URLs for attribution | feed |
| Include List Price | Original price when special price active | Yes |
| Include Barcodes | Read EAN/UPC/GTIN attributes | Yes |
| Seller Name | Store/brand name in ACP response | Store name |
| Terms of Service URL | Linked in seller.links | — |
| Privacy Policy URL | Linked in seller.links | — |
| Refund Policy URL | Linked in seller.links | — |
| Shipping Policy URL | Linked in seller.links | — |
| FAQ URL | Linked in seller.links | — |
Quick start
# 1. Create a feed
curl -X POST https://yourstore.com/rest/V1/angeo/product_feeds \
-H "Content-Type: application/json" \
-d '{"targetCountry":"US","storeId":1}'
# → {"id":"feed_a1b2c3d4e5f6","target_country":"US","store_id":1,"updated_at":"...","created_at":"..."}
# 2. Get products (page 1, 100 per page)
curl "https://yourstore.com/rest/V1/angeo/product_feeds/feed_a1b2c3/products?page=1&pageSize=100"
# 3. Upsert products (POST to /upsert; OpenAI PATCH is rewritten by nginx)
curl -X POST https://yourstore.com/rest/V1/angeo/product_feeds/feed_a1b2c3/products/upsert \
-H "Content-Type: application/json" \
-d '{"feedId":"feed_a1b2c3","products":[{"id":"SKU_42","variants":[{"id":"SKU_42_BLK","title":"Black"}]}]}'
# → {"id":"feed_a1b2c3","accepted":true,"upserted_count":1,"errors":[]}
# 4. Get promotions (sourced from Magento SalesRules)
curl https://yourstore.com/rest/V1/angeo/product_feeds/feed_a1b2c3/promotions
# 5. Invalidate product cache (after catalog changes)
curl -X POST https://yourstore.com/rest/V1/angeo/product_feeds/feed_a1b2c3/products/invalidate \
-H "Authorization: Bearer <admin_token>"
ACP Product Schema Coverage
Product level
| Field | Source |
|---|---|
id |
product.entity_id — stable, never changes |
title |
product.name |
description.plain |
short_description stripped, or description stripped |
description.html |
description raw HTML |
url |
Product URL + utm_medium=feed&utm_source=chatgpt |
media |
Gallery images via ImageUrlBuilder |
Variant level
| Field | Source |
|---|---|
id |
Child entity_id for configurable, product entity_id otherwise |
title |
Child or parent product name |
price |
final_price in minor units (cents) |
list_price |
price when special_price is active (per ACP best practices) |
availability |
StockRegistry → in_stock / out_of_stock |
categories |
Category names with merchant taxonomy |
variant_options |
Configurable attribute labels/values (color, size, …) |
barcodes |
ean / upc / gtin / barcode / isbn product attributes |
condition |
["new"] (override in ProductMapper) |
seller.name |
Config or store name |
seller.links |
ToS, privacy, refund, shipping, FAQ from config |
ACP Promotion Schema Coverage
Sourced from active Magento SalesRules:
| Magento SalesRule | ACP field |
|---|---|
rule_id |
id as promo_rule_{id} |
name |
title |
description + coupon code |
description.plain |
is_active + dates |
status: active / scheduled / expired / disabled |
from_date / to_date |
active_period.start_time / end_time (RFC 3339) |
by_percent |
{type:"percent_off", percent_off: N} |
by_fixed / cart_fixed |
{type:"amount_off", amount_off:{amount, currency}} |
free_shipping flag |
{type:"free_shipping"} (combinable with other benefits) |
Testing
vendor/bin/phpunit -c app/code/Angeo/OpenAiProductFeedApi/phpunit.xml
The Angeo AI Suite
| Module | Purpose |
|---|---|
angeo/module-aeo-audit |
AEO audit — 8 signals scored |
angeo/module-llms-txt |
Generates /llms.txt |
angeo/module-openai-product-feed |
CSV product feed file generator |
angeo/module-openai-product-feed-api |
This module — ACP REST API |
License
MIT — see LICENSE
No changelog yet
The vendor hasn't published a changelog. Tagged releases appear in the Versions tab.
| Version | Stability | QA Status | Released |
|---|---|---|---|
| 1.0.0 | stable | Incomplete | 2026-04-18 19:19:00 |
Requires 9
| Package | Constraint |
|---|---|
| angeo/module-openai-product-feed | ^1.0 |
| magento/framework | * |
| magento/module-backend | * |
| magento/module-catalog | * |
| magento/module-catalog-inventory | * |
| magento/module-configurable-product | * |
| magento/module-sales-rule | * |
| magento/module-store | * |
| php | >=8.2 |
Requires-dev 1
| Package | Constraint |
|---|---|
| phpunit/phpunit | ^10.0 |
Suggests 2
| Package | Reason |
|---|---|
| angeo/module-aeo-audit | Audit your full AEO + AI feed readiness. |
| angeo/module-llms-txt | Generate llms.txt for AI content map. |
No QA results yet
QA pipelines haven't run for this version. Status appears here once the vendor publishes a tagged release that gets ingested.
Turn an existing module into recurring revenue.
If you already maintain a Magento 2 module on GitHub or GitLab, listing it on Packagento takes about five minutes. We mirror your tags, handle distribution signing, and route paid licenses through Stripe Connect, so you can keep shipping the way you already do.