Widget tokens live 60 minutes (was 10). POST /widget/init reports exp_minutes: 60.
New error code widget_token_invalid (401) for a missing/expired/foreign-origin X-Widget-Token on every widget route (previously Invalid widget token / forbidden). It is distinct from unauthorized (user token): re-run /widget/init and retry instead of showing a sign-in. Fixes the sign-in loop in the hosted save dialog when the token expired during e-mail confirmation.
Popup URL carries key and origin: the SDK (v1.1.0, also the legacy save-sdk.js) and the Basket SDK now pass the public key and the page origin to /save and /basket, so the hosted pages can renew their token themselves. The open event payload gained popup: boolean; the blocking "opened as a tab" alert() is gone and off() really unsubscribes.
/widget/init rejects a body origin that differs from the request's Origin header (400 invalid_origin, reason: origin_mismatch).
OAuth bridge: /widget/oauth-state and /widget/oauth-poll require the widget token (X-Widget-Token) or public key (X-Widget-Key); states are bound to that key, single-use (409 state_consumed), valid 10 minutes, and the poll only releases tokens to the creating key (403 state_key_mismatch).
POST /baskets/{id}/event requires the basket owner's public key (X-Widget-Key header or ?key=); GET /baskets/{id} only returns rendering settings.
429 on abuse-sensitive routes: /widget/init, /widget/oauth-* and /baskets/{id}/event now enforce per-IP and per-key per-minute limits with a Retry-After header. All other routes keep the informational headers only.
Correction: /sdk/save-sdk.js does not redirect. It is the deprecated legacy script and stays served unchanged (security fixes only); migrate to /sdk/v1/widget.js.
Canonical SDK URL: https://shareawish.shop/sdk/v1/widget.js (basket: /sdk/v1/basket-integration.js). /sdk/save-sdk.js, cdn.shareawish.shop/sdk/v1/* and cdn.shareawish.com/widget.js are deprecated. (Corrected 2026-09-19: /sdk/save-sdk.js stays served as-is, it is not redirected.)
Allow-list rule for new live keys: POST /widget/init only accepts a pk_live_ key from origins on the key's domain allow-list. Test keys keep working on localhost.
Pricing v2: new plan structure in the Partner Portal; rate-limit and usage headers unchanged, plan figures are exposed via GET /api-usage/{apiKeyId}.
Documentation of environment: test keys and the informational (non-blocking) rate-limit model.
v1.0 – Initial release
Save Button SDK with hosted save flow (/widget/init, /widget/save, /widget/saved-products).
HTML data-shareawish attributes and programmatic window.ShareAWish.init() API, Shopify Liquid snippets.
Basket Integration SDK for partner shops (/baskets/{id}, /baskets/{id}/event).
Partner Portal with API key management, domain allow-lists and usage dashboard.