Skip to content

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/.

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.

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 the sellingpartnerapi::notifications scope — 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 via transmit_after instead of blocking a Sidekiq thread.

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_*.

  • 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_number semantics differ per marketplace: the US seller catalog keys by seller SKU, the CA catalog stores the ASIN there with the base SKU on items.sku (Amazon-side CA seller SKUs carry a -CA suffix 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::Sync and refreshed per item by ListingIssueRefreshWorker.
  • AmazonProductTypeSchema — cached product-type definition schemas used to build listing feed payloads.
  • AmazonVariation / AmazonVariationMarketplace — per-marketplace variation (parentage) tracking.
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.

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.

  • 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.