Skip to content

Amazon BFF Route — Import API

POST /api/amazon/import is a server-only BFF route in arda-frontend-app that accepts an Amazon ASIN or product page URL, calls Amazon’s Creators API, and returns a stable v1 DTO representing the product. It is the data-acquisition endpoint for the “Create Item from Amazon URL” workflow (PDEV-446 / PDEV-445).

The route acts as a faithful proxy/adaptor for the Amazon Creators API GetItems response: it maps the upstream fields into a stable Arda DTO without imposing stricter field-presence requirements than Amazon does. Amazon’s GetItems responses are sparse for some ASINs (particularly digital items); nullable DTO fields are passed through as null rather than rejected. The caller receives the best information Amazon provides.

The route is not directly callable by end users; it is consumed by the SPA (a follow-up project). It has no user-facing surface in v1.

Tenant-scoped Cognito JWT — identical to all /api/arda/* routes. The Authorization: Bearer <token> header is required. Anonymous requests are rejected with HTTP 401 to protect Arda’s Amazon API quota.

POST /api/amazon/import
Content-Type: application/json
Authorization: Bearer <cognito-id-token>

Body:

{ "input": "<ASIN or Amazon product URL>" }

input is required. Accepted forms:

FormExample
Bare ASINB08N5WRWNW
/dp/<ASIN> (canonical)https://www.amazon.com/dp/B08N5WRWNW
/<slug>/dp/<ASIN>/...https://www.amazon.com/Some-Product/dp/B08N5WRWNW/ref=sr_1_1
/gp/product/<ASIN>https://www.amazon.com/gp/product/B08N5WRWNW
/gp/aw/d/<ASIN> (mobile)https://www.amazon.com/gp/aw/d/B08N5WRWNW

Arbitrary query parameters are stripped before ASIN extraction. Only US amazon.com URLs are accepted.

HTTP 200 is returned when all four primary fields — name, image, price, and productUrl — are populated in Amazon’s response.

HTTP 206 (Partial Content) is returned when any of name, image, price, or productUrl is null. The DTO is still well-formed (nullable fields preserved); the 206 status signals to callers that Amazon’s response was sparse for this ASIN. unitCount, unit, and upc being null does NOT trigger 206 — those are routinely absent even on fully-cooperative Amazon responses (e.g. digital items and software).

Example full response (HTTP 200):

{
"ok": true,
"data": {
"name": "Some Product Name",
"image": {
"url": "https://m.media-amazon.com/images/I/...",
"width": 500,
"height": 500
},
"price": {
"amount": 19.99,
"currency": "USD",
"displayAmount": "$19.99"
},
"unitCount": 12,
"unit": "32 oz",
"upc": "012345678901",
"asin": "B08N5WRWNW",
"productUrl": "https://www.amazon.com/dp/B08N5WRWNW?linkCode=ogi&tag=ardacards-20&th=1&psc=1"
}
}

Example sparse response (HTTP 206 — price and productUrl absent from Amazon’s upstream response):

{
"ok": true,
"data": {
"name": "Some Digital Product",
"image": {
"url": "https://m.media-amazon.com/images/I/...",
"width": 500,
"height": 500
},
"price": null,
"unitCount": null,
"unit": null,
"upc": null,
"asin": "B08N5WRWNW",
"productUrl": null
}
}

The v1 DTO type is defined in arda-frontend-app/src/lib/shared/amazon/types.ts (AmazonImportDto).

FieldTypeNullableNotes
namestring | nullYesItem display title; null when Amazon’s response omits itemInfo.title
image{ url: string; width: number; height: number } | nullYesPrimary image at Large size; null when Amazon’s response omits images.primary.large. When present, url is Amazon-CDN-hosted and passed through unchanged
price{ amount: number; currency: string; displayAmount: string } | nullYesCurrent Buy Box price; null when Amazon returns no Buy Box listing. When present, currency is "USD" for US-only v1; displayAmount is pre-formatted (e.g. "$19.99")
unitCountnumber | nullYesInteger pack count when Amazon exposes one (routinely absent for digital items)
unitstring | nullYesFree-text size / unit-of-measure string (routinely absent for digital items)
upcstring | nullYesUniversal Product Code; first value when Amazon returns multiple (routinely absent for digital items)
asinstringNo10-character Amazon ASIN — always populated
productUrlstring | nullYesAmazon detailPageURL verbatim when present — see Affiliate-Tag Rule below; null when Amazon’s response omits the field

When productUrl is non-null, it is Amazon’s detailPageURL passed through unchanged. Amazon embeds Arda’s partnerTag and tracking parameters (linkCode, language, th, psc) into this URL; these must not be stripped, rebuilt, or modified. Verbatim Amazon guidance (API Rates page, 2026-05-07):

“You are using the links provided by Creators API when linking back to Amazon. Do not edit any of the URL parameters.”

When productUrl is null, Amazon’s response did not include detailPageURL for this ASIN. The route does not synthesise a fallback URL.

The buildAffiliateUrl() utility in src/lib/shared/amazon/affiliate-url.ts constructs a minimal URL from an ASIN for cases where no Creators API response is available (e.g. PDEV-429 cart-link work) — it is not used to produce productUrl in v1.

Error responses use stable machine-readable codes plus human-readable default messages. The SPA can override the message copy; the code field is the stable contract.

{
"ok": false,
"code": "AMAZON_ITEM_NOT_ACCESSIBLE",
"message": "The requested Amazon item is not available via the API."
}
CodeHTTP StatusWhen
AUTHENTICATION_REQUIRED401Authorization header missing or JWT invalid/expired
INVALID_REQUEST400Request body is not valid JSON, or the input field is missing or not a string
UNSUPPORTED_SHORT_LINK422Request body is valid and input is a string, but the value is an a.co or amzn.to short link — redirect-following not supported in v1
UNRECOGNIZED_AMAZON_URL422Request body is valid and input is a string, but the value cannot be parsed as a recognisable ASIN or Amazon product URL
UNSUPPORTED_AMAZON_LOCALE422input resolves to a non-US Amazon host (e.g. amazon.co.uk, amazon.de)
AMAZON_ITEM_NOT_ACCESSIBLE404Amazon returned HTTP 404 ResourceNotFoundException — item not found or restricted
AMAZON_API_THROTTLED429Amazon returned HTTP 429 ThrottleException — TPS/TPD quota exceeded
AMAZON_API_UNAVAILABLE502Amazon API failure (5xx), network failure, credential misconfiguration, or eligibility loss

INVALID_REQUEST (HTTP 400) is the structural guard: it fires before any ASIN parsing and covers malformed JSON, missing input, or a non-string input value. UNRECOGNIZED_AMAZON_URL and the other 422s only fire when the request body is structurally valid.

AMAZON_API_UNAVAILABLE on a 401 or 403 from Amazon is alarm-worthy and indicates a credential or eligibility issue, not a transient failure.

The route reads the following Creators API resource paths to populate the DTO. Only the v1 minimum set is requested:

itemInfo.title
itemInfo.productInfo
itemInfo.externalIds
images.primary.large
offersV2.listings.price
offersV2.listings.isBuyBoxWinner

Full mapping table — per context-exploration.md §“v1 BFF DTO ← Creators API field mapping”:

DTO fieldCreators API source path
nameitemInfo.title.displayValue
image.urlimages.primary.large.url
image.widthimages.primary.large.width
image.heightimages.primary.large.height
price.amountoffersV2.listings[0].price.money.amount
price.currencyoffersV2.listings[0].price.money.currency
price.displayAmountoffersV2.listings[0].price.money.displayAmount
unitCountitemInfo.productInfo.unitCount.displayValue
unititemInfo.productInfo.size.displayValue
upcitemInfo.externalIds.upcs.displayValues[0]
asinasin (top-level)
productUrldetailPageURL (top-level) — verbatim pass-through

Four server-side environment variables are required. They are aggregated through src/lib/env.ts. All have mock-value fallbacks in mock mode.

VariableDescriptionSource
AMAZON_CREATORS_CREDENTIAL_IDOAuth2 client ID1Password vault Arda-*OAM → Amplify env var
AMAZON_CREATORS_CREDENTIAL_SECRETOAuth2 client secret1Password vault Arda-*OAM → Amplify env var
AMAZON_CREATORS_CREDENTIAL_VERSIONCredential version string (e.g. "3.1")1Password vault Arda-*OAM → Amplify env var
AMAZON_ASSOCIATE_TAGAmazon Associates partner tag1Password vault Arda-*OAM → Amplify env var

These variables must be present in the Amplify app’s branch environment before the route becomes operational. They are added to the env | grep allowlist in amplify.yml so Amplify writes them to the build’s .env.

The US marketplace identifier "www.amazon.com" is a source constant defined in src/lib/shared/amazon/constants.ts. It is not a runtime env var — v1 supports US only (goal.md Constraint 6).

amplify.yml includes these four variable names in the env | grep filter:

-e AMAZON_CREATORS_CREDENTIAL_ID -e AMAZON_CREATORS_CREDENTIAL_SECRET -e AMAZON_CREATORS_CREDENTIAL_VERSION -e AMAZON_ASSOCIATE_TAG
src/
lib/
shared/amazon/
types.ts # AmazonImportDto + AmazonImportErrorCode types
constants.ts # MARKETPLACE constant
asin.ts # extractAsin() — ASIN from URL or bare ASIN
affiliate-url.ts # buildAffiliateUrl() — fallback builder (not used for productUrl)
server/
lib/amazon/
creators-client.ts # fetchCreatorsApiItems() — thin wrapper around amazon-creators-api SDK
routes/amazon/
import.ts # importFromAmazon() — orchestration + DTO mapping
app/
api/amazon/import/
route.ts # Next.js Route Handler — JWT + JSON → importFromAmazon → response
  • No in-process response caching. Each request triggers a fresh Creators API call. The IP License permits caching non-image content for up to 24 hours; v1 opts for no cache to keep the implementation simple.
  • Image URL is not re-hosted. image.url is Amazon’s CDN-hosted URL passed through unchanged. The IP License prohibits storing or caching images. Downstream callers must respect the 24-hour caching ceiling on stored URLs.
  • Single ASIN per call in v1. The Creators API client signature accepts a list of ASINs for future bulk use, but the v1 route always calls with one ASIN.
  • Eligibility risk. Creators API access is conditional on generating qualified referring sales every 30 days. See context-exploration.md §“API Rates, Quotas & Eligibility”.