Amazon SP-API Integration
How Heatwave talks to Amazon's Selling Partner API (SP-API): authentication,
the EDI pipeline that moves catalog/order/pricing data, the scheduled workers
that drive it, and the real-time notification layer that complements them.
Companion doc: Amazon SP-API Notifications (SQS + EventBridge).
Working conventions live in the amazon-marketplace and retailer-onboarding
skills under .agents/skills/.
The two Amazon relationships
| Seller Central (3P) | Vendor Central (1P) | |
|---|---|---|
| Model | We sell on Amazon's marketplace | Amazon buys wholesale from us |
| Code | Edi::Amazon::* |
Edi::AmazonVc::* |
| Flows | Listings feeds, orders, pricing, buy box, listing issues, product types, A+ content | Purchase orders, acknowledgements, shipping labels, invoices, packing slips, catalog data, direct-fulfillment suppression |
| Marketplaces | 12 profiles: US, CA, MX (NA account) + FR, BE, DE, ES, IT, NL, PL, SE, UK (EU account) | US, CA |
Both share the same transport class and the same EDI pipeline shape.
Authentication & transport
Everything goes through
Transport::HttpSellerApiConnection.
Per-marketplace credentials live in Rails encrypted credentials, read via
Heatwave::Configuration.fetch(:amazon_sc_seller_api) (NA) /
(:amazon_sc_seller_eu_api) (EU) — each carries the LWA client_id /
client_secret / refresh_token, api_host, and legacy SigV4 keys.
- LWA access token (
fetch_access_token): refresh-token grant, scoped to
the selling-partner account. Used for nearly all operations. - Grantless token (
fetch_grantless_access_token):client_credentials
grant with thesellingpartnerapi::notificationsscope — required for
Notifications API destination management. - Restricted Data Tokens: GET operations on PII paths (orders, shipments,
labels…) exchange the access token for an RDT scoped to that one call. - SigV4 signing: legacy requirement, still applied; harmless.
- Sandbox vs production host: dev credentials carry the sandbox
api_host.production_host_required?routes GET reads, restricted-token,
/feeds/, and/notifications/calls to the production host (sandbox data
is canned junk; the notifications sandbox persists nothing), while other
dev POSTs deliberately stay on the sandbox. - 429 handling: rate-limit responses are not retried in-band — they
bubble up so the EDI layer reschedules viatransmit_afterinstead of
blocking a Sidekiq thread.
The EDI pipeline
All SP-API traffic is logged and processed through
EdiCommunicationLog (ECL) rows — the same pattern as Wayfair/Walmart:
Orchestrator (per partner: profiles, endpoints, marketplace ids)
│
├── Retrievers pull from SP-API → create ECL (category, data, state: ready)
├── Processors consume ready ECLs → update domain records
└── Senders build outbound payloads (feeds) → transmit → ECL
ECL states (ready → processing → processed / retry / exception / archived)
drive retries and give the CRM UI (EDI Communication Logs) reprocess and
search. Categories of interest: listing_feed_data, price_advice,
inventory_advice, listing_item_information, catalog_item_information,
buy_box_status, order_batch, fba_order_batch, a_plus_content_*.
Key models
CatalogItem— one row per (catalog, item); Amazon state lives here
(is_amz_buy_box_winner,amazon_fba_sku,amazon_fnsku,
third_party_part_number). ⚠️third_party_part_numbersemantics differ
per marketplace: the US seller catalog keys by seller SKU, the CA catalog
stores the ASIN there with the base SKU onitems.sku(Amazon-side CA
seller SKUs carry a-CAsuffix that is not stored locally).AmazonMarketplace— marketplace id (ATVPDKIKX0DER= US,
A2EUQ1WTGCTBG2= CA, …) →has_many :catalogs; the routing key between
Amazon payloads and our catalogs.ListingIssue— normalized listing problems per catalog item, synced by
ListingIssues::Syncand refreshed per item byListingIssueRefreshWorker.AmazonProductTypeSchema— cached product-type definition schemas used
to build listing feed payloads.AmazonVariation/AmazonVariationMarketplace— per-marketplace
variation (parentage) tracking.
Scheduled flows (America/Chicago)
| Worker | Schedule | What it does |
|---|---|---|
| Listings feeds — three hourly crons: price at :00, inventory at :30, listing at :45 | each hourly (offsets stagger the 1-feed-per-5-min rate limit) | Differential JSON_LISTINGS_FEED submissions; skip when nothing changed |
AmazonCatalogItemListingsDataWorker |
daily 03:00 | Full listings-data pull for US/CA items older than 1 day |
AmazonPricingAutomationWorker |
daily 06:15 | Repricing pass, 3 h after fresh listings data |
| (retired 2026-08-01) | — | AmazonBuyBoxRecoveryWorker polled buy-box offers hourly for every non-winner. The ANY_OFFER_CHANGED notification now drives correction within seconds, and the nightly pass reconciles anything it missed — see Catalog::AmazonBuyBoxService |
AmazonOrderStatusVerificationWorker |
daily 18:00 | Verifies recently-invoiced orders show Shipped on Seller Central |
AmazonRefreshProductTypeSchemasWorker |
weekly Sun 02:00 | Refreshes product-type schemas for active/pending items |
AmazonSqsNotificationPollerWorker |
every minute | Drains SP-API push notifications — see the notifications doc |
AmazonVcDirectFulfillmentCatalogItemSuppressionDataWorker |
daily 04:00 | Vendor Central direct-fulfillment suppression data |
Orders flow continuously through the EDI order retrievers/processors
(marketplace order pull, FBA order batches, acknowledgements, shipping
confirmation), and feed results are processed by EdiStatusFlowWorker.
Notifications vs pulls
Since 2026-07 the pull sweeps are complemented by real-time SP-API
notifications (SQS + EventBridge → webhook_logs). The operating principle:
notifications are the primary detection signal; pulls demote to
reconciliation backstops as each subscription phase lands (Amazon delivers
at-least-once, best-effort — never delete the pull code). The staged
cadence-reduction plan and the Rails Event Store convergence design (one
domain event per fact — e.g. buy box lost — regardless of source) live in
doc/tasks/202607152125_AMAZON_SPAPI_SQS_NOTIFICATIONS.md.
Operational pointers
- ECL UI: CRM → EDI Communication Logs (filter by partner
amazon_seller_central_*, category, state; reprocess button). - Webhook UI: CRM → Webhook Logs (provider
amazon_sp_api) for
notifications. - Listing issues: CRM catalog-item pages + the daily retailer compliance
report. - Product-type reclassification cases: tracked 1:1 as Basecamp "Amazon
Tickets" — see.agents/skills/amazon-marketplace/references/product-type-cases.md. - Rate limits: JSON_LISTINGS_FEED is 1 submission / account / 5 min — the
hourly feed crons are offset for exactly this reason.