Skip to content

Context: v1 Decision Backing

This document collects the legal, contractual, and technical context that supports v1 decisions, with citations to original sources (Amazon Associates legal documents, Amazon Creators API documentation, court rulings, and the Apache-2.0-licensed amazon-creators-api npm wrapper). It is self-contained: a future reader can verify every quoted passage against the cited URL or document.

Each section is included only if it backs a decision recorded in this directory’s goal.md.

SourceAuthorityURL / CitationFetchedVerification status
Associates Program IP License (Sections 2(h), 2(i))Amazonhttps://affiliate-program.amazon.com/help/operating/policies2026-05-07Direct fetch. Document version date stated on the page: April 14, 2026. Verbatim quotes captured below.
Associates Program Operating AgreementAmazonhttps://affiliate-program.amazon.com/help/operating/agreement2026-05-07Direct fetch. Document version date: October 15, 2025. Section 1 + Section 12 confirm Program Policies (including the IP License) are incorporated by reference.
PA-API 5 deprecation bannerAmazonhttps://webservices.amazon.com/paapi5/documentation/get-items.html2026-05-06Direct fetch. Cited only to justify the choice of Amazon Creators API over the deprecated PA-API 5. No technical specifics are taken from PA-API 5 documentation.
Amazon Creators API — API Reference indexAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/api-reference2026-05-07Fetched via JS-rendered browser (Playwright). Lists operations (GetBrowseNodes, GetItems, SearchItems, GetVariations) and high-level resources (BrowseNodeInfo, BrowseNodes, Images, ItemInfo, ParentASIN, SearchRefinements, VariationSummary).
Amazon Creators API — GetItems referenceAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/api-reference/operations/get-items2026-05-07Fetched via JS-rendered browser. Verbatim request parameters, resource enumeration, and example response captured.
Amazon Creators API — OffersV2 resourceAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/api-reference/resources/offersV2 (note: camelCase URL)2026-05-07Fetched via JS-rendered browser. Verbatim attribute table, Money shape, and Offers v1 → OffersV2 mapping captured.
Amazon Creators API — ItemInfo resourceAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/api-reference/resources/item-info2026-05-07Fetched via JS-rendered browser. Verbatim attribute tables for byLineInfo, classifications, externalIds, features, productInfo, title, etc.
Amazon Creators API — Images resourceAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/api-reference/resources/images2026-05-07Fetched via JS-rendered browser. Verbatim shape: images.primary.{small,medium,large}.{url, height, width}.
Amazon Creators API — Using SDKAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/get-started/using-sdk2026-05-07Direct fetch. Documents that the official SDK is distributed as a downloadable archive (Node.js, Python, PHP, Java); not on npm. SDK changelog through 1.2.0 (2026-02-22) captured.
Amazon Creators API — Register for Creators APIAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/onboarding/register-for-creators-api2026-05-07Direct fetch via JS-rendered browser. Captures: prerequisites (final-acceptance Associates account + qualified-sales standing), portal path (Tools > Creators API), credential issuance flow, quotas (max 2 applications per store × 2 credential sets per application), primary-account-owner restriction.
Amazon Creators API — API RatesAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/concepts/api-rates2026-05-07Fetched via JS-rendered browser. Verbatim TPS/TPD definitions, initial 1 TPS / 8640 TPD allocation, scaling rule (+1 TPS per $4,320 shipped revenue, capped at 10), 30-day eligibility-loss rule, “Do not edit any of the URL parameters” guidance.
Amazon Creators API — Error Codes & MessagesAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/troubleshooting/error-codes-and-messages2026-05-07Fetched via JS-rendered browser. Verbatim error response shape, HTTP-status-aligned exception types (ValidationException, UnauthorizedException, AccessDeniedException, ResourceNotFoundException, ThrottleException, InternalServerException), reason codes per type, token-endpoint rate limit (v2 only: 300 req / 5 min), token-cache imperative.
Amazon Creators API — Migrating from PA-APIAmazonhttps://affiliate-program.amazon.com/creatorsapi/docs/en-us/migrating-to-creatorsapi-from-paapi2026-05-07Fetched via JS-rendered browser. Source for the data API host (creatorsapi.amazon), endpoint path pattern (/catalog/v1/<op>), required headers (Authorization, Content-Type, x-marketplace), and the v2 Authorization: Bearer <token>, Version <version> format.
amazon-creators-api npm package sourceRyan Chiang (Apache-2.0)https://github.com/ryanschiang/amazon-creators-api, https://www.npmjs.com/package/amazon-creators-api2026-05-07Cloned and inspected at version 1.2.2 (published 2026-03-07). Republishes Amazon’s official Node.js SDK source plus a thin TypeScript wrapper (TypedDefaultApi, ~180 lines of method-forwarding with JSDoc-derived .d.ts). Native fetch replaces superagent. License: Apache 2.0; NOTICE.txt attributes original authorship to Amazon. Source for the OAuth v2/v3 endpoint enumeration and 30-second pre-expiry buffer documented in §“Authentication & Credentials” below.
GitHub mirrors of Amazon’s official SDK (alternative-vendoring fallback if the npm wrapper goes silent)Amazon (sources) / community (mirrors)https://github.com/Adaptive-Media/creatorsapi-nodejs-sdk (created 2026-01-30, Apache-2.0), https://github.com/zerbfra/creatorsapi-nodejs-sdk-amazon (created 2026-03-05, Apache-2.0)2026-05-07Verified existence via gh repo view. Both are git-committed snapshots of Amazon’s official creatorsapi-nodejs-sdk archive. Useful as drop-in replacements if amazon-creators-api becomes unavailable; the wrapper’s value-add (TypeScript types + native fetch) would have to be re-implemented locally.
Meta Platforms, Inc. v. Bright Data Ltd.U.S. District Court for the Northern District of CaliforniaSummary judgment for Bright Data on the breach-of-contract claim, January 23, 2024; tortious-interference claim dropped February 23, 2024n/a (case citation)Secondary reading: Proskauer client release at https://www.proskauer.com/release/proskauer-secures-dismissal-of-scraping-claims-against-bright-data; Lowenstein Sandler client alert at https://www.lowenstein.com/news-insights/publications/client-alerts/meta-v-bright-data-ruling-has-important-implications-for-webscraping-activities-by-investment-advisers-im.
X Corp. v. Bright DataU.S. District Court for the Northern District of CaliforniaRuling, 2024n/a (case citation)Secondary reading: Skadden insight at https://www.skadden.com/insights/publications/2024/05/district-court-adopts-broad-view.

Drift check. Before relying on this document months from now, re-fetch the Amazon URLs above and diff against the verbatim quotes below. Watch the document version dates printed on the IP License and Operating Agreement pages and re-verify when they change. The Creators API pages do not advertise version dates; treat the captured material as accurate as of the fetch date and re-verify when implementation begins.

API Choice: Amazon Creators API (not PA-API 5)

Section titled “API Choice: Amazon Creators API (not PA-API 5)”

Decision. v1 builds against Amazon’s Creators API. Product Advertising API 5 is being deprecated; building against it ships dead code.

Verbatim deprecation banner (from https://webservices.amazon.com/paapi5/documentation/get-items.html, fetched 2026-05-06):

“PA-API will be deprecated on May 15th, 2026. Please migrate to Creators API.”

Amazon designates the Creators API as the named successor for Associates / publishers / influencers. The Creators API documentation root is a JavaScript-rendered SPA at https://affiliate-program.amazon.com/creatorsapi/docs/; pages are accessible to humans in a browser but anti-bot heuristics return HTTP 403 to non-JS-executing fetchers. v1’s audit trail (this document) captures the relevant pages via a JS-capable browser at the dates listed in the Source Provenance table.

The Creators API uses an OAuth 2.0 client_credentials flow. The credential model has two coexisting variants:

VariantCredential formatToken endpoint patternOAuth scope
v2.x (Cognito)Credential ID + Secret + Version, 2.xhttps://creatorsapi.auth.<aws-region>.amazoncognito.com/oauth2/token (region per .1 / .2 / .3)creatorsapi/default
v3.x (Login with Amazon)Credential ID + Secret + Version, 3.xhttps://api.amazon.<tld>/auth/o2/token (TLD per locale: .com for US, .co.uk, .co.jp)creatorsapi::default

The credential Version string is issued by Amazon at credential creation and uniquely identifies the auth path. New credentials are expected to be v3.x LWA; older v2.x Cognito credentials remain supported for compatibility.

For Arda’s US-only v1, the modern path is v3.1 LWA:

  • Token endpoint: https://api.amazon.com/auth/o2/token
  • Grant type: client_credentials
  • Scope: creatorsapi::default
  • Request body: JSON {grant_type, client_id, client_secret, scope}
  • Response: {access_token, expires_in, ...} — token TTL is on the order of one hour; client implementations apply a 30-second pre-expiry buffer before refreshing.

(Source: src/auth/OAuth2Config.js and src/auth/OAuth2TokenManager.js in amazon-creators-api@1.2.2 — see Source Provenance.)

Implementation note (v1). The BFF imports the official SDK (re-published via the amazon-creators-api npm wrapper) which abstracts both v2/v3 endpoints; v1 code passes the credential triple {credentialId, credentialSecret, version} and the SDK selects the correct endpoint and request format. v1 does not need to fetch the token endpoint URL directly.

(Source: Migrating-from-PA-API page, fetched 2026-05-07.)

Verbatim from Amazon’s migration guide:

“Endpoints: webservices.amazon.com/paapi5/...creatorsapi.amazon/catalog/v1/...

So:

  • Data API host: creatorsapi.amazon (uses Amazon’s .amazon TLD; not creatorsapi.amazon.com).
  • Endpoint path pattern: /catalog/v1/<operation> — e.g. /catalog/v1/getItems, /catalog/v1/searchItems.
  • Required request headers:
    • Authorization: Bearer <token> (v3 LWA) or Authorization: Bearer <token>, Version <version> (v2 Cognito).
    • Content-Type: application/json.
    • x-marketplace: www.amazon.com — required as a header in addition to the body field of the same name.
  • Request body: JSON, lowerCamelCase keys.

For v1 (using the amazon-creators-api SDK wrapper), all of the above is abstracted by the SDK — the BFF code only specifies the credential triple, the marketplace string, and the getItems request payload. The host, path, and header construction live inside src/api/DefaultApi.js of the SDK and are documented here so any future hand-rolled fallback or troubleshooting has a verbatim reference.

(Source: API Rates page, fetched 2026-05-07.)

Verbatim definitions:

“TPS — Transactions per second, refers to the maximum number of API calls you can make in one second. Each API call counts as one transaction. For example, if you send 10 ASINs in the request parameter of a GetItems() call, it counts as a single transaction.”

“TPD — Transactions per day, refers to the maximum number of API calls you can make in one day. If Associate has 1 TPS and 8640 TPD, then maximum of 1 request can be sent per second and 8640 per day. Even if 1 TPS is there, once TPD is exhausted requests will be throttled.”

Initial allocation (verbatim): “an initial usage limit up to a maximum of one request per second (one TPS) and a cumulative daily maximum of 8640 requests per day (8640 TPD) for the first 30-day period.”

Scaling formula (verbatim): “Your account will earn a usage limit of one TPD for every five cents or one TPS (up to a maximum of ten TPS) for every $4320 of shipped item revenue generated via the use of Creators API for shipments in the previous 30-day period (this automatic allocation amount is updated daily).”

Eligibility loss (verbatim, operationally important): “your account will lose access to Creators API if it has not generated qualified referring sales for a consecutive 30-day period. If you lose access to Creators API, you can continue to use other product linking tools, such as Site Stripe and generate revenue. You will regain access to Creators API within two days after your referred sales are shipped.”

Best practices to preserve attribution (verbatim, the bullet that drives the affiliate-tag rule reversal in §“Affiliate-Tag Canonicalisation Rule” below):

  • “You are using the links provided by Creators API when linking back to Amazon. Do not edit any of the URL parameters.”
  • “Your Associate account and Creators API account were created using the same Amazon account (i.e. email address).”
  • “You are using your primary account to make requests to Creators API.”
  • “You are passing your Partner tag in all your requests to Creators API. We cannot retroactively apply any sales credit to your account if you forget to do this correctly.”

Throttle response shape (verbatim from Error Codes page):

HTTP/1.1 429 Too Many Requests
{
"type": "ThrottleException",
"message": "The request was denied due to request throttling. Please verify the number of requests made per second.",
"retryAfterSeconds": 60
}

The BFF honours retryAfterSeconds when present; otherwise applies exponential backoff.

For Arda’s US scale. v1’s BFF is human-driven (one paste at a time per user) so steady-state TPS is far below any realistic cap. The 10 TPS ceiling matters only if a future bulk-import path is added (out of v1 scope) or if many users paste simultaneously. The 30-day eligibility-loss rule is an operational risk: if Arda’s qualified sales drop to zero for a month (e.g. an outage of the Create-from-Amazon flow), Creators API access pauses — recorded in goal.md Open Items.

Token Caching — Critical Operational Guidance

Section titled “Token Caching — Critical Operational Guidance”

(Source: Error Codes & Messages page, fetched 2026-05-07.)

Verbatim from Amazon’s documentation:

“Cache the access token until it expires; reuse it for every Creators API call.”

“If you run multiple processes or containers under the same Client ID, share one cache across them.”

“Consider migrating to the official SDKs — they handle token caching and renewal automatically.”

The amazon-creators-api wrapper’s OAuth2TokenManager already implements single-process in-memory caching with a 30-second pre-expiry buffer. The “share one cache across processes” guidance is not addressed by the SDK — each Lambda container / Node.js worker maintains its own token. v1’s BFF runs on AWS Amplify’s compute, which spawns multiple isolated workers; each worker therefore independently fetches its own token at first use.

Is single-process caching sufficient for v1?

  • Yes for v3 LWA (Arda’s chosen path): the v3 token endpoint (api.amazon.com/auth/o2/token) is not subject to the 300 req / 5 min limit that applies to v2 Cognito endpoints. With the per-worker cache + 1-hour TTL, even worst-case cold-start storms produce a manageable rate.
  • Marginal for v2 Cognito: 300 req / 5 min divided across workers means cold-start storms could theoretically exceed the limit. v1 will not use v2; documented for completeness.

If a future hardening pass introduces a shared token cache (e.g. via Redis / ElastiCache), it must respect the 30-second pre-expiry buffer (refresh slightly before the recorded expiresAt).

(Summary; see the operator-facing runbook deliverable named in goal.md for the full step-by-step.)

Prerequisites for Creators API registration (verbatim from Amazon’s “Register for Creators API” page, fetched 2026-05-07):

“Before you register for the Creators API, you must have an Amazon Associates account that has been reviewed and received final acceptance into the Amazon Associates Program.”

“Creators API sign up is available only to associates who have referred qualified sales and have been accepted into the program.”

Arda’s existing US Associates standing meets this prerequisite.

Sign-up flow: Associates Central → ToolsCreators APICreate Application → name it → Add New Credential → copy or download Credential ID + Credential Secret + Version (CSV).

Quotas:

  • Max 2 applications per Associates store.
  • Max 2 credential sets per application.
  • Total: 4 credential sets per store.

Operational notes:

  • Only the primary account owner of the Associates account can sign up for / manage Creators API credentials. The engineering team cannot do this on the operator’s behalf.
  • The Application is bound to the Associates store’s marketplace (US for Arda).
  • Credential sets can be deleted; the application can only be deleted after all its credential sets are removed.

v1 credential strategy: one Application named for the Arda integration; a single credential set (Credential ID, Secret, Version) reused across all four partition vaults (Arda-DevOAM, Arda-StageOAM, Arda-DemoOAM, Arda-ProdOAM) per goal.md Constraint 12. Each vault holds its own independent entry so any future per-environment rotation requires no infrastructure change. The Application’s quota of two credential sets is therefore unused in v1; a second set can be issued later if/when per-environment isolation is wanted.

SDK Choice — amazon-creators-api (Apache-2.0 wrapper)

Section titled “SDK Choice — amazon-creators-api (Apache-2.0 wrapper)”

Decision. v1 imports amazon-creators-api@1.2.2 from npm and pins the version exactly (no ^, no ~).

What it is. A republish of Amazon’s official creatorsapi-nodejs-sdk source (which Amazon distributes only as a downloadable archive, not via npm) plus a thin TypedDefaultApi TypeScript wrapper that adds .d.ts types via JSDoc-derived tsc -p tsconfig.types.json. The wrapper drops the upstream SDK’s superagent dependency in favour of native fetch().

License. Apache 2.0 with proper NOTICE.txt attribution to Amazon for the underlying SDK code.

Maturity (as of 2026-05-07). Created 2026-03-07; latest version 1.2.2 published 2026-03-07; 0 stars / 0 forks / 0 issues / 0 PRs; single maintainer (ryanschiang).

Why this choice over hand-rolling or vendoring Amazon’s archive:

  • Coverage of fiddly bits. The wrapper handles v2-Cognito-vs-v3-LWA endpoint branching, scope name flipping (creatorsapi/default vs creatorsapi::default), JSON-vs-form-encoded request bodies per version, and the 30-second pre-expiry buffer. Hand-rolling would re-create all of this; vendoring Amazon’s archive ships those plus superagent’s dependency tree.
  • Type quality is real. The .d.ts files are generated from JSDoc on the upstream SDK source; type quality matches Amazon’s JSDoc, which covers the request/response/model classes we touch (GetItemsRequestContent, GetItemsResponseContent, Item, OffersV2, ItemInfo, Money, ImageType, etc.).
  • Native fetch is a real improvement over the upstream SDK’s superagent dependency.

Risks accepted, with mitigations:

  • Single-maintainer / new package. Pin amazon-creators-api@1.2.2 exactly. Commit package-lock.json with integrity hashes. Audit on every bump.
  • Supply-chain trust. The wrapper’s own code is ~180 trivial lines (TypedDefaultApi.js is method forwarding); the bulk is Amazon’s auto-generated SDK source, auditable against the official archive.
  • Documented downgrade path. If the wrapper goes silent, abandoned, or is compromised, the fallback is to vendor Amazon’s official creatorsapi-nodejs-sdk archive as a workspace package under arda-frontend-app/packages/amazon-creators-sdk/. The wrapper’s interface (ApiClient, TypedDefaultApi, GetItemsRequestContent, GetItemsResource) is small enough to swap without rewriting business logic — estimated half a day of work.

(Decision rationale recorded against the existing arda-frontend-app convention, fetched 2026-05-07 from arda-frontend-app/amplify.yml, arda-frontend-app/CLAUDE.md, arda-frontend-app/src/lib/env.ts, and the existing process.env.* usages elsewhere in the repo.)

What’s secret vs operationally tied vs invariant

Section titled “What’s secret vs operationally tied vs invariant”
ValueSecret?Lifecyclev1 v1 source
Credential SecretHard secret — equivalent to a password. Compromise → full Amazon impersonation as Arda.Rotates with Credential ID + Version (issued as a triple by Amazon).1Password vault entry, single multi-field.
Credential IDPublic-by-design (= client_id).Rotates with the Secret.Same vault entry.
Version (e.g. "3.1")Public.Rotates with the Secret.Same vault entry.
Associate TagPublic; appears in every affiliate URL.Tied to the Associates account, not to a credential. Per-program (per-locale), not per-environment. v1: single Arda US tag.Same vault entry (kept with the credential family for atomic operator updates).
Marketplace ("www.amazon.com")Public. Constant.Never rotates (US-only by goal.md Constraint 6).Source constant in src/lib/shared/amazon/constants.ts.

Variable naming (matches existing arda-frontend-app pattern)

Section titled “Variable naming (matches existing arda-frontend-app pattern)”

Existing process.env.* usages in the repo (sampled 2026-05-07): ARDA_API_KEY, ARDA_SIGNUP_SECRET_KEY, COGNITO_CLIENT_ID, COGNITO_CLIENT_SECRET, COGNITO_REGION, COGNITO_USER_POOL_ID, HUBSPOT_API_BASE, HUBSPOT_PRIVATE_ACCESS_TOKEN, PYLON_WIDGET_SECRET, CLOUDFRONT_KEY_PAIR_ID. The pattern is <SERVICE>_<PROPERTY> for server-only values, no NEXT_PUBLIC_ prefix; client-exposed values (browser-bundled) use the NEXT_PUBLIC_ prefix.

For the Creators API the four variables match this pattern:

VariableSideSource-of-truth
AMAZON_CREATORS_CREDENTIAL_IDserver-only (no NEXT_PUBLIC_ prefix)1Password vault → Amplify branch env var
AMAZON_CREATORS_CREDENTIAL_SECRETserver-only1Password vault → Amplify branch env var
AMAZON_CREATORS_CREDENTIAL_VERSIONserver-only1Password vault → Amplify branch env var
AMAZON_ASSOCIATE_TAGserver-only1Password vault → Amplify branch env var

The AMAZON_CREATORS_* prefix scopes the credential triple so future Amazon things (e.g. AWS service credentials) won’t collide.

Delivery: 1Password (source of truth) → Amplify env vars (runtime), via the existing IaC pipeline

Section titled “Delivery: 1Password (source of truth) → Amplify env vars (runtime), via the existing IaC pipeline”

The Creators API credentials use the same delivery pipeline that arda-frontend-app already uses for its other server-side credentials (ARDA_API_KEY, HUBSPOT_PRIVATE_ACCESS_TOKEN, etc.). The pipeline (verified against infrastructure/amm.sh, infrastructure/src/main/cfn/amplify.cfn.yaml, infrastructure/src/main/cfn/partitionSecrets.cfn.yaml on 2026-05-07):

1Password vault entry
↓ (op read at deploy time)
amm.sh (deploy script)
↓ (CFN parameter override)
partitionSecrets.cfn.yaml — creates AWS::SecretsManager::Secret resources, exports their ARNs
↓ (CloudFormation cross-stack !ImportValue + {{resolve:secretsmanager:…:SecretString:<key>::}})
amplify.cfn.yaml — EnvironmentVariables block on the AWS::Amplify::App resource
↓ (Amplify build picks up env vars; amplify.yml's `env | grep` filter writes them to .env)
arda-frontend-app/amplify.yml allowlist
Next.js BFF: process.env.AMAZON_*

Settled v1 decisions (per goal.md Q1 / Q2 in the SDK / IaC round):

  1. Source of truth: per-partition 1Password vaults. Each amm.sh partition deploy reads from its own vault — Arda-DemoOAM (Alpha001/demo), Arda-DevOAM (Alpha002/dev), Arda-StageOAM (Alpha002/stage), Arda-ProdOAM (Alpha001/prod). Even when v1 places the same credential triple in all four, the per-partition source path is preserved so a future iteration can rotate one independently without touching the other partitions’ code paths.
  2. AWS Secrets Manager: one multi-field secret per partition. Each partition’s partitionSecrets CFN stack creates a single AWS::SecretsManager::Secret named ${Infrastructure}-${Partition}-AmazonCreatorsApi whose SecretString is a JSON object with four keys: credentialId, credentialSecret, version, associateTag. This matches the 1Password “single multi-field entry per vault” choice and the rotation-as-a-tuple semantics. The repo already uses field-extract syntax for Cognito’s web client secret ({{resolve:secretsmanager:${SecretArn}:SecretString:clientSecret::}}), so the pattern is established.
  3. Amplify env vars: four CFN entries. amplify.cfn.yaml’s EnvironmentVariables block gains four entries — AMAZON_CREATORS_CREDENTIAL_ID, AMAZON_CREATORS_CREDENTIAL_SECRET, AMAZON_CREATORS_CREDENTIAL_VERSION, AMAZON_ASSOCIATE_TAG — each resolving from the partition’s AmazonCreatorsApi secret via field-extract syntax against the cross-stack-imported ARN.
  4. Local dev: op run resolves the same fields from the same vault into process.env.* for engineers running npm run dev locally; matches the existing convention recorded in arda-frontend-app/knowledge-base/local-dev-and-preview-testing.md (e.g. op://Arda-DevOAM/Amazon Creators API/credentialId).
  5. amplify.yml allowlist: Amplify’s preBuild step has an explicit env | grep -e ... >> .env filter; the four AMAZON_* names must be added to that allowlist in the same PR as the BFF code, otherwise the build produces a .env without them and the BFF fails at runtime. Per the warning at the top of amplify.yml, the same allowlist update must also be applied to each Amplify app’s inline build spec via aws amplify update-app --app-id <id> --build-spec "$(cat amplify.yml)"; whether that command is wrapped by IaC depends on the per-partition IaC compliance (see “IaC Compliance per Partition” section below).
  6. Server-side aggregation in the BFF: all four are read through arda-frontend-app/src/lib/env.ts (the existing requiredEnv() aggregator) so mock-mode and tests have safe fallbacks — same pattern as ARDA_API_KEY and the Cognito family.

Why “non-secret in source, secret in vault” is not the chosen split

Section titled “Why “non-secret in source, secret in vault” is not the chosen split”

A natural-sounding split would put the three non-secret values (Credential ID, Version, Associate Tag) in a constants file and only the Credential Secret in 1Password. That is rejected for v1 because:

  • Drift risk on the credential triple. ID + Secret + Version are issued as a single triple by Amazon’s “Create Application + Add Credential” flow and rotate together. Splitting them across source and vault means a rotation requires two coordinated changes (code PR + vault edit). Forget one and the BFF runs with a mismatched triple → 401 UnauthorizedException.
  • Atomic operator workflow. “Operator updates one vault entry, redeploys” beats “Operator updates vault entry AND opens a PR.”
  • Marketplace IS in source (as a const), because it is a true invariant fixed by Constraint 6, not a credential-family value. That’s the one place the “non-secret in source” pattern applies.

Acceptable alternative if a future iteration prefers uniformity: move MARKETPLACE into env vars too (e.g. AMAZON_MARKETPLACE) and have all five values in 1Password. The cost is one extra Amplify env var and an entry in src/lib/env.ts; the gain is symmetry. v1 keeps the constant in source for readability.

IaC Compliance per Partition (verified against infrastructure/amm.sh)

Section titled “IaC Compliance per Partition (verified against infrastructure/amm.sh)”

(Source: infrastructure/amm.sh, infrastructure/.github/workflows/amm.yml, the CFN templates under infrastructure/src/main/cfn/, and infrastructure/src/main/cdk/constructs/oam/gh-oidc-provider.ts. Verified 2026-05-07.)

The four partitions are not uniformly IaC-managed. Some Amplify apps were created before the IaC practice was established; they have a different deploy path. v1 must replicate both patterns to land env vars on all four partitions.

amm.sh defines AMPLIFY_DEPLOY_TARGETS containing Alpha001:demo (and historically SandboxKyle002:kyle, which is no longer in service and is out of scope for this project). Only the partitions in that list get the standard CloudFormation Amplify-app deploy:

  • partitionSecrets.cfn.yaml (Secrets Manager secret per credential).
  • amplify.cfn.yaml (the AmplifyApp resource with its EnvironmentVariables block referencing each secret via {{resolve:secretsmanager:${SecretArn}:SecretString[:<key>]::}} against a cross-stack !ImportValue of the secret’s ARN).
  • amplifyBranch.cfn.yaml (branch + custom domain).

For these partitions, adding a new env var means editing amplify.cfn.yaml’s EnvironmentVariables block — that’s it. The next CFN deploy materialises the env var on the Amplify app.

Partial-IaC partitions (manually-created Amplify apps)

Section titled “Partial-IaC partitions (manually-created Amplify apps)”

Alpha001:prod, Alpha002:dev, Alpha002:stage are not in AMPLIFY_DEPLOY_TARGETS. Their Amplify apps were created before IaC and are not declared by amplify.cfn.yaml. The else branch of amm.sh (introduced by PR #438, “feat: add Amplify compute role for manually-created apps and image CDN env vars”) handles them via:

  1. Deploy a standalone amplifyComputeRole.cfn.yaml stack (the IAM compute role, the only IaC asset for these apps).
  2. Read the existing Amplify app’s environmentVariables map: aws amplify get-app --app-id <id> --query "app.environmentVariables".
  3. Merge new env vars in via jq (preserving existing values — update-app REPLACES the whole map, so the merge step is mandatory).
  4. Write back: aws amplify update-app --app-id <id> --environment-variables "<merged-json>".

The current merge expression in amm.sh injects INFRASTRUCTURE, PARTITION, NEXT_PUBLIC_INFRASTRUCTURE, NEXT_PUBLIC_PARTITION, and conditionally CLOUDFRONT_KEY_PAIR_ID (only if its CFN export exists). For our project, the same jq block must be extended to merge in the four AMAZON_* values, sourced from the partition’s AmazonCreatorsApi Secrets Manager secret via aws secretsmanager get-secret-value + jq field extraction.

The exports for these manually-created apps come from amplifyExports.cfn.yaml — a “no-op placeholder” stack whose only purpose is to publish the existing Amplify App ID and Branch Name as CloudFormation exports (so cross-stack !ImportValue works for downstream stacks like the compute role).

(Source: infrastructure/src/main/cdk/constructs/oam/gh-oidc-provider.ts:375-376, amm.sh:245, commit 9bb6954 “Add us-east-2 Amplify permissions to frontend IAM role”.)

Alpha001:prod’s Amplify app runs in us-east-2 while every other partition’s Amplify app runs in us-east-1. The gh-oidc-provider.ts IAM policy carries an explicit comment: “Prod Amplify app is in us-east-2 (historical anomaly); grant access to both regions” — and adds arn:aws:amplify:us-east-2:${account}:apps/* to the GitHub-Actions role’s permissions in addition to the default-region ARN.

Region selection at deploy time is per partition, not per stack. The amm.yml GitHub Actions workflow:

  1. Reads the partition’s region from a partition-config service (Arda-cards/purpose-configuration-action@v1) — the partition’s properties file is the single source of truth for aws_region.
  2. Configures AWS credentials with that region.
  3. Invokes amm.sh with the partition arg.

Inside amm.sh, every aws cloudformation deploy, aws cloudformation list-exports, aws amplify get-app, and aws amplify update-app call inherits the per-partition region from the environment. So for Alpha001:prod, the entire deploy — partitionSecrets, amplifyComputeRole, the env-var merge call — all target us-east-2. There is no cross-region resolution needed; the partition is internally consistent.

The map AMPLIFY_REGION_OVERRIDES=([Alpha001:prod]="us-east-2") declared at amm.sh:244-246 carries an explicit comment: “Not used directly by amm.sh — referenced by GitHub Actions workflows (arda-frontend-app) as the single source of truth for region mappings.” So this map is consumed by arda-frontend-app’s deploy workflow when issuing direct Amplify API calls (e.g. start-job); amm.sh itself doesn’t read it.

Implication for v1. Replicating the existing partial-IaC + region-aware pattern is enough; no new region-bridging logic is required. The Secrets Manager secret for Alpha001:prod lives in us-east-2 (same partition); the aws secretsmanager get-secret-value call in amm.sh’s partial-IaC block runs in us-east-2; the resulting aws amplify update-app call runs in us-east-2. End-to-end consistent.

Concrete infrastructure PR — file-by-file changes

Section titled “Concrete infrastructure PR — file-by-file changes”
FileChange
src/main/cfn/partitionSecrets.cfn.yamlAdd a AmazonCreatorsApi parameter (MinLength: 1, NoEcho: true, JSON-shaped value {"credentialId":"…","credentialSecret":"…","version":"…","associateTag":"…"}). Add AmazonCreatorsApiSecret resource (AWS::SecretsManager::Secret, name ${Infrastructure}-${Partition}-AmazonCreatorsApi, SecretString: !Ref AmazonCreatorsApi). Add AmazonCreatorsApiArn output exported as ${Infrastructure}-${Partition}-I-AmazonCreatorsApiArn.
src/main/cfn/amplify.cfn.yamlIn the AmplifyApp resource’s EnvironmentVariables block, add four entries — AMAZON_CREATORS_CREDENTIAL_ID, AMAZON_CREATORS_CREDENTIAL_SECRET, AMAZON_CREATORS_CREDENTIAL_VERSION, AMAZON_ASSOCIATE_TAG — each resolving via !Sub - "{{resolve:secretsmanager:${SecretArn}:SecretString:<key>::}}" - SecretArn: !ImportValue Fn::Sub: "${Infrastructure}-${Partition}-I-AmazonCreatorsApiArn".
amm.sh(a) Add an op read of the four fields from each partition’s vault (op://Arda-${PartitionVault}/Amazon Creators API/{credentialId,credentialSecret,version,associateTag}). (b) Combine into a single JSON string and pass as AmazonCreatorsApi=... parameter override on the existing partitionSecrets CFN deploy. (c) For partitions in the partial-IaC else branch, extend the JQ merge to fetch the secret via aws secretsmanager get-secret-value --secret-id "${infrastructure}-${partition}-AmazonCreatorsApi", jq the four fields, and merge them into the update-app --environment-variables payload.
.github/workflows/amm.ymlNo change needed if local-dev path is acceptable; if the workflow needs to source the credential triple from GitHub org secrets (rather than 1Password directly), add AMAZON_CREATORS_CREDENTIAL_* and AMAZON_ASSOCIATE_TAG to the env: block. (To be confirmed in specification.md; the current amm.yml already passes ARDA_API_KEY and friends through GitHub org secrets.)
(no change to gh-oidc-provider.ts)The existing IAM grants for amplify:GetApp / amplify:UpdateApp on arn:aws:amplify:*:apps/* already cover both regions — see line 375-376 comment.

The infrastructure PR is small and additive; it slots into the established pattern without introducing new mechanisms.

Section 2(h) of the Associates Program IP License (from https://affiliate-program.amazon.com/help/operating/policies, document version April 14, 2026, fetched 2026-05-07):

“You will not store or cache Product Advertising Content consisting of an image, but you may store a link to Product Advertising Content consisting of an image for up to 24 hours.”

Implication for v1. The BFF route returns Amazon’s image URL unchanged in its response. v1 does not download the image, copy it to S3, proxy it, or in any other way “store or cache” it. Downstream callers (the follow-up project’s persistence layer) are bound by the License’s 24-hour rule on stored URLs.

IP License — 24-Hour Caching for Non-Image Content

Section titled “IP License — 24-Hour Caching for Non-Image Content”

Section 2(h), continuing (same source):

“You may store other Product Advertising Content that does not consist of images for caching purposes for up to 24 hours, but if you do so you must immediately thereafter refresh and re-display the Product Advertising Content by making a call to Creators API, PA API or retrieving a new Data Feed and refreshing the Product Advertising Content on your application immediately thereafter.”

Implication for v1. The BFF must not memoise Creators API responses for longer than 24 hours. The recommended v1 posture is no in-process cache at all — every request triggers a fresh upstream call. Any future memoisation layer must honour the 24-hour ceiling and the explicit “refresh and re-display” obligation.

IP License — Pricing / Availability Disclaimer (informational, future use)

Section titled “IP License — Pricing / Availability Disclaimer (informational, future use)”

Section 2(i) of the same License (verbatim):

“Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.”

Not in v1 scope (no UI in v1), but recorded so the follow-up project plans the disclaimer copy alongside any price-display affordance.

Section 2(h) of the same License (verbatim):

“Unless otherwise notified by us, you may store individual Amazon Standard Identification Numbers (ASINs) for an indefinite period until the termination of this License.”

Implication. The follow-up project may persist ASINs on Item / ItemSupply records indefinitely. v1 does not persist ASINs (no Item creation in scope); the rule is recorded for the follow-up project.

Decision. The BFF’s DTO productUrl is the detailPageURL value Amazon returns from getItems, passed through unchanged.

Verbatim guidance from Amazon’s API Rates page (https://affiliate-program.amazon.com/creatorsapi/docs/en-us/concepts/api-rates, fetched 2026-05-07, “Best Practices” section):

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

Rationale. Amazon embeds Arda’s partnerTag plus a number of tracking parameters (linkCode=ogi, language=en_US, th=1, psc=1) into detailPageURL. These extra parameters carry attribution data Amazon needs to credit Arda for the click. Stripping them or rebuilding the URL from the ASIN alone risks losing credit on resulting sales — an explicitly-prohibited modification per the quote above.

Earlier (parked-exploration) framing was wrong. The Amazon-Client-Integration (Extended) project recommended rebuilding the URL from the ASIN as a defence-in-depth measure. That recommendation is invalidated by Amazon’s own published guidance — keep the URL Amazon emits, do not modify it.

Scope of the rule: the same “pass through unchanged” treatment applies to all URLs in the Creators API response — detailPageURL, images.primary.<size>.url, and any other media or resource URL — none of them are re-canonicalised, signed, or otherwise rewritten by the BFF.

The Shared affiliate-url.ts builder utility (which constructs https://www.amazon.com/dp/<ASIN>?tag=<assoc-tag> from an ASIN) is still useful as a fallback for cases where Amazon’s response is unavailable — notably PDEV-429 cart-link work, where the SPA needs to construct an add-to-cart URL from ASINs the user has selected without first calling getItems. v1 does not use this builder for the BFF response.

Tag-presence invariant. Arda’s partnerTag is sent to Amazon in the request and Amazon returns it embedded in detailPageURL. The invariant “every product-page URL the BFF returns carries Arda’s tag” is preserved by Amazon’s response shape, not by Arda-side rebuilding.

Decision. v1 supports amazon.com (US) only. Non-US Amazon hosts (amazon.co.uk, amazon.de, amazon.ca, …) are rejected at the BFF with the structured error UNSUPPORTED_AMAZON_LOCALE.

Three system-level reasons:

  1. Per-locale Associates programs. The Associates Program Operating Agreement (https://affiliate-program.amazon.com/help/operating/agreement, version October 15, 2025) governs participation in a single locale’s program; each locale (US, UK, DE, etc.) is a separate program with its own enrolment, its own commission table, and its own Associate Tag namespace. Arda’s tag is a US-program tag; placing it on amazon.co.uk URLs does not credit Arda for any sale. Multi-locale support requires Arda to enrol in each locale’s program and to maintain a per-locale tag map.
  2. Per-locale Creators API credentials and endpoints. The Creators API issues credentials per Associates store, which itself is bound to a single marketplace. v3 LWA token endpoints are locale-pinned (api.amazon.com, api.amazon.co.uk, api.amazon.co.jp); v2 Cognito endpoints are region-pinned. A US credential cannot return a UK listing.
  3. IP License grant is per program. The IP License grant to display Amazon Product Advertising Content is tied to the program Arda is enrolled in (US). Displaying UK content under a US-only enrolment falls outside the License’s grant.

A fourth, smaller reason: ASIN identity is locale-scoped at the catalog level. A given 10-character ASIN string can refer to different items in amazon.com vs amazon.co.uk, or exist in one and not the other.

Unblock conditions (any one):

  • Arda enrols in another locale’s Associates program (gains a per-locale tag).
  • Arda gains credentials for another Creators API store / region.
  • Product roadmap commits to multi-marketplace, justifying the engineering cost.

The single Shared utility src/lib/shared/amazon/asin.ts accepts the following forms in v1. All can be parsed by regex without network calls; short links (which would require server-side redirect-following) are rejected with a clear error.

The forms below are synthesised from observed Amazon URL patterns; Amazon does not publish a canonical specification of “all the URLs that resolve to a product page,” so this list is empirical and is the v1 specification for our parser.

Accepted forms (US amazon.com only):

FormExampleParse strategy
Bare ASINB08N5WRWNWIf input matches /^[A-Z0-9]{10}$/, accept directly.
/dp/<ASIN> (canonical)https://www.amazon.com/dp/B08N5WRWNWRegex \/dp\/([A-Z0-9]{10}) against the URL path.
/<title-slug>/dp/<ASIN>/…https://www.amazon.com/Some-Product-Name/dp/B08N5WRWNW/...Same regex; slug ignored.
/gp/product/<ASIN>https://www.amazon.com/gp/product/B08N5WRWNWRegex \/gp\/product\/([A-Z0-9]{10}).
/gp/aw/d/<ASIN> (mobile)https://www.amazon.com/gp/aw/d/B08N5WRWNWRegex \/gp\/aw\/d\/([A-Z0-9]{10}).

Arbitrary query parameters (?ref=…, ?tag=…, ?keywords=…, ?th=…, session IDs, etc.) are stripped before parsing.

Rejected forms (return structured error):

FormError codeReason
https://a.co/d/…UNSUPPORTED_SHORT_LINKRequires server-side redirect-following (not in v1).
https://amzn.to/…UNSUPPORTED_SHORT_LINKSame.
Search / cart / homepage URLsUNRECOGNIZED_AMAZON_URLNo single-product target.
https://amazon.co.uk/..., https://amazon.de/..., etc.UNSUPPORTED_AMAZON_LOCALESee “US-Only Marketplace Scope” above.
Any URL where the regex cannot extract a 10-character [A-Z0-9]{10} ASINUNRECOGNIZED_AMAZON_URLNo usable identifier.

Forms left out of v1 deliberately (not currently in the wild or already supported by the canonical regex):

  • smile.amazon.com/… — Amazon Smile was discontinued in 2023; defunct.
  • amazon.com/-/<lang>/… — locale-prefix variant; rare; the canonical regex still extracts the ASIN if encountered.
  • amazon.com/exec/obidos/asin/…, amazon.com/o/ASIN/…, amazon.com/dp/product/… — archaic legacy forms; effectively unused by current Amazon URLs.

Creators API GetItems — v1 Request and Resource Set

Section titled “Creators API GetItems — v1 Request and Resource Set”

The v1 BFF calls GetItems with the minimum resource set that maps to the v1 DTO (see goal.md Scope > In Scope > BFF route for the DTO). The request shape below is verbatim from the Amazon-published GetItems operation page.

Required parameters (from Amazon docs, fetched 2026-05-07):

  • itemIds — list of strings, up to 10 (v1 always sends 1).
  • partnerTag — non-empty string (Arda’s Associate Tag, from BFF server-side config).

Optional parameters used in v1:

  • itemIdType — default "ASIN".
  • marketplace — set to "www.amazon.com" (US only per scope decision).
  • resources — explicit list (see below).

(partnerType, merchant, offerCount from PA-API 5 do not exist in Creators API.)

v1 resources array:

[
"itemInfo.title",
"itemInfo.productInfo",
"itemInfo.externalIds",
"images.primary.large",
"offersV2.listings.price",
"offersV2.listings.isBuyBoxWinner"
]

Creators API GetItems — Response Field Paths Used in v1

Section titled “Creators API GetItems — Response Field Paths Used in v1”

The response shape below is verbatim from Amazon’s published examples (operation page + the relevant resource pages, all fetched 2026-05-07). v1 reads only the paths listed.

{
"itemsResult": {
"items": [
{
"asin": "<ASIN>",
"detailPageURL": "...", // verbatim pass-through to BFF DTO `productUrl` (per goal Constraint 3)
"images": {
"primary": {
"large": { "url": "...", "height": <int>, "width": <int> }
}
},
"itemInfo": {
"title": { "displayValue": "...", "label": "Title", "locale": "en_US" },
"productInfo": {
"unitCount": { "displayValue": <int>, "label": "NumberOfItems", "locale": "en_US" },
"size": { "displayValue": "...", "label": "Size", "locale": "en_US" }
// (other productInfo attributes ignored)
},
"externalIds": {
"upcs": { "displayValues": ["..."], "label": "UPC", "locale": "en_US" }
// (eans, isbns ignored in v1)
}
},
"offersV2": {
"listings": [
{
"isBuyBoxWinner": true,
"price": {
"money": { "amount": <BigDecimal>, "currency": "USD", "displayAmount": "$..." }
// (pricePerUnit, savingBasis, savings ignored in v1)
}
}
]
}
}
]
},
"errors": [
{ "code": "ItemNotAccessible", "message": "..." } // present when an itemId cannot be resolved
]
}

Important OffersV2 note (verbatim from Amazon’s OffersV2 resource page): “OffersV2 provides only featured listings as shown to retail customers on Amazon Detail Page buybox, which represent the best overall offers for the product. If there are no featured offers, OffersV2 will show status as out of stock.” Therefore listings[] typically contains a single entry that is the Buy Box; v1 reads listings[0].

BFF DTO fieldCreators API source pathNotes
nameitemInfo.title.displayValueString. Truncate to operations’ Item.name length cap if needed (cap settled in spec).
image.urlimages.primary.large.urlAmazon-CDN-hosted URL; passed through unchanged.
image.widthimages.primary.large.widthInteger pixel count.
image.heightimages.primary.large.heightInteger pixel count.
price.amountoffersV2.listings[0].price.money.amountBigDecimal in JSON; serialised as a number.
price.currencyoffersV2.listings[0].price.money.currencyISO 4217 string. "USD" for US-only v1.
price.displayAmountoffersV2.listings[0].price.money.displayAmountPre-formatted display string (e.g. "$19.99").
unitCountitemInfo.productInfo.unitCount.displayValueInteger pack count. Nullable when Amazon does not expose one.
unititemInfo.productInfo.size.displayValueFree-text size / unit-of-measure string. Nullable when Amazon does not expose one.
upcitemInfo.externalIds.upcs.displayValues[0]Take the first when multiple UPCs are returned. Nullable when Amazon does not return any.
asinasin (top-level)Always present on a successful item entry.
productUrldetailPageURL (top-level on each item)Amazon’s response URL passed through unchanged, per the API Rates “Best Practices” verbatim quote (“Do not edit any of the URL parameters”). Carries Arda’s partnerTag plus Amazon’s tracking parameters (linkCode=ogi, language, th, psc).

(Source: https://affiliate-program.amazon.com/creatorsapi/docs/en-us/troubleshooting/error-codes-and-messages, fetched 2026-05-07.)

Creators API returns structured error responses with HTTP-status-aligned exception types. Important difference from PA-API 5: when a single ASIN cannot be resolved in GetItems, the response is HTTP 404 ResourceNotFoundException at the request level — not an inline errors[] entry. Inline errors[] entries still appear in multi-ASIN responses where some ASINs succeed and some fail (HTTP 200 + partial results + per-failed-ASIN errors[].code: "ItemNotAccessible"). v1 always sends one ASIN, so the dominant failure path is the request-level HTTP 404.

Verbatim error response structure:

{
"type": "<ExceptionType>",
"message": "<human-readable description>",
"reason": "<machine-readable code>", // for ValidationException, AccessDeniedException, UnauthorizedException
"fieldList": [...], // optional; ValidationException with FieldValidationFailed
"resourceId": "...", // optional; ResourceNotFoundException
"resourceType": "...", // optional; ResourceNotFoundException
"retryAfterSeconds": <int> // optional; ThrottleException
}

HTTP-status-to-exception mapping (verbatim):

HTTPException typeWhen it fires (verbatim summary)
400ValidationException”The input fails to satisfy the constraints specified by the service.” Reason codes: UnknownOperation, CannotParse, FieldValidationFailed (with fieldList), InvalidAssociate, InvalidPartnerTag, Other.
401UnauthorizedException”Missing or bad authentication for the operation.” Reason codes: TokenExpired, InvalidToken, InvalidIssuer, MissingClaim, MissingKeyId, UnsupportedClient, InvalidClient, MissingCredential, Other.
403AccessDeniedException”User does not have sufficient access to perform this action.” Reason codes: AssociateNotEligible, AuthorizationFailed, Other.
404ResourceNotFoundException”Request references a resource which does not exist.” Includes resourceType and resourceId.
429ThrottleException”Request was denied due to request throttling.” Includes optional retryAfterSeconds.
500InternalServerException”Unexpected error during processing of request. Clients should retry with exponential backoff.”

Token-endpoint rate limit (v2.x Cognito only, verbatim from Amazon). v2 token endpoints rate-limit each client to 300 requests per 5 minutes:

“The v2.x Cognito token endpoints (creatorsapi.auth..amazoncognito.com/oauth2/token) rate-limit each client to 300 requests per 5 minutes. This is the token endpoint itself — not Creators API — so the response shape differs from the errors above.”

“Access tokens are valid for 1 hour (expires_in: 3600). A correctly implemented client requests at most one token per hour, per credential, which is well under the limit. Hitting this error almost always means you are fetching a new token on every API call instead of reusing the cached one.”

The v3.x LWA endpoints (api.amazon.com/auth/o2/token, etc.) are not subject to this specific limit. Arda will use v3 LWA, so this limit is informational rather than a design constraint.

Creators API conditionBFF error code (per goal.md Constraint 7)
HTTP 404 ResourceNotFoundException (single-ASIN call where the ASIN cannot be resolved)AMAZON_ITEM_NOT_ACCESSIBLE
HTTP 200 with inline errors[].code = "ItemNotAccessible" (multi-ASIN partial failure; not in v1’s call pattern but possible)AMAZON_ITEM_NOT_ACCESSIBLE
HTTP 429 ThrottleExceptionAMAZON_API_THROTTLED (honour retryAfterSeconds if present)
HTTP 500 InternalServerException, network failure, or token-acquisition failureAMAZON_API_UNAVAILABLE
HTTP 401 UnauthorizedException (any reason)AMAZON_API_UNAVAILABLE (alarm-worthy — indicates token-cache bug or credential drift)
HTTP 400 ValidationException with reason = "InvalidPartnerTag" or "InvalidAssociate"AMAZON_API_UNAVAILABLE (alarm-worthy — credential / tag misconfiguration)
HTTP 400 ValidationException with other reasonsAMAZON_API_UNAVAILABLE (alarm-worthy — Arda-side bug; should not appear in production)
HTTP 403 AccessDeniedException with reason = "AssociateNotEligible"AMAZON_API_UNAVAILABLE (alarm-worthy — eligibility loss; see API Rates section for the 30-day rule)

Decision. v1 does not use third-party “Amazon product data” services (Rainforest API, ScraperAPI, Bright Data, Oxylabs, SerpApi, Apify, etc.) as a fallback or parallel data source. The official Creators API (via the amazon-creators-api wrapper) is the only data path.

Rationale (grounded in Amazon’s contractual terms and recent case law):

  • The IP License’s grant to display Amazon Product Advertising Content is tied to content obtained through approved channels (PA-API / Creators API / Data Feeds; the License names these explicitly in the non-image-caching clause quoted above). Content scraped from the public site sits outside the grant — meaning Arda has no Amazon-conferred display right at all for it.
  • Amazon’s Conditions of Use prohibit “using any automated process or technology to access … any part of the Amazon Website.” Recent case law — Meta Platforms, Inc. v. Bright Data Ltd. (N.D. Cal., summary judgment for Bright Data, January 23, 2024) and X Corp. v. Bright Data (N.D. Cal., 2024) — protects the act of scraping by the provider, but does not confer a license to display the resulting content. Associates members displaying scraped content are exposed to a separate, additional breach: no licensed display + auto-acquisition prohibition + Associates-account-termination grounds.
  • Image rehosting via a scraper is strictly worse than image rehosting via the Creators API: scraped images are acquired outside the License entirely, so there is not even a claim to the 24-hour link allowance documented above.
  • Account termination would also kill the cart-link feature tracked in PDEV-429, since affiliate-tagged URLs require a live Associates account.

The two situations that justify third-party scrapers — (a) the consumer carries no Associates relationship to lose, (b) the consumer needs catalog data the official API does not expose (full prose descriptions, A+ content, deep variation trees, full review bodies) — are both inverse to Arda’s situation: Arda’s Associates account is a meaningful asset, and v1’s needs are the catalog basics that Amazon exposes through the Creators API.

The following narrow items remain to be verified once Arda’s credentials are issued and the BFF makes its first live call:

  • Container key consistency (itemsResult vs itemResults). Amazon’s own documentation uses both spellings across different example responses on the same site (see e.g. the GetItems example response which uses itemResults, vs the OffersV2 and ItemInfo examples which use itemsResult). The amazon-creators-api SDK’s GetItemsResponseContent model exposes typed accessors that abstract this; first live response confirms the actual on-the-wire key and that the SDK matches it.
  • x-marketplace header verification. Amazon’s migration guide documents that x-marketplace is required as a header in addition to the body field. The SDK should set this automatically; verify on first live call by inspecting outbound headers via SDK debug logs.
  • Localisation behaviour if currencyOfPreference / languagesOfPreference are ever set (out of v1 scope; recorded for future multi-locale work).

Copyright: (c) Arda Systems 2025-2026, All rights reserved