Developer Changelog
Public, developer-facing contract changes for the Throttle API, embedded checkout, webhooks, and SDKs. Internal refactors and UI-only changes are excluded.
2026-06-21 — Cart email capture + richer cart.abandoned payload
API
- Cart
customerEmail.POST /api/v1/cartsandPATCH /api/v1/carts/{id}accept an optionalcustomerEmail— lightweight email capture without a full customer record. The cart response now also returnscustomerEmailand the storedshippingAddress/billingAddress.
Webhooks
- Enriched
cart.abandoned. The payload now includesshippingAddressandbillingAddress(as stored on the cart, ornull), andcustomernow represents a guest captured via the cart’scustomerEmailas{ id: null, email, firstName: null }— so anonymous carts with a captured email are recoverable. All fields remain additive.
SDK
@usethrottle/cart:CreateCartInput/UpdateCartInput/CartgaincustomerEmail.@usethrottle/webhook-types:CartAbandonedDatagains the address fields and a nullable customer id.
2026-06-20 — useThrottleCheckout hook
SDK
@usethrottle/checkout-react. NewuseThrottleCheckouthook that orchestrates a storefront checkout over a cart session: totals andselectedMethodbound to the cart, one-callselectMethod, astatusstate machine, automatic stale-cart recovery (rebuild + retry oncart_not_open), andcreateSessionreturning thecheckoutSessionIdfor<PaymentEmbed>. See Cart sessions.
2026-06-20 — allowedMethods on payment-only embeds
API
- Fail-loud.
POST /api/v1/checkout-sessions/embed-token(payment-only) now rejectsallowedMethodswith400 allowed_methods_unsupportedinstead of accepting and silently ignoring it. A payment-only Gr4vy embed renders the methods configured on your Gr4vy connection; the embed token has no method-restriction field.allowedMethodscontinues to filter the full hosted checkout (the/payment-methodscatalog + payment tiles) — that flow is unchanged.
SDK
@usethrottle/checkout-sdk.createEmbedTokenno longer acceptsallowedMethods(it never applied to the payment-only embed).createSessionstill accepts it for the full checkout.
Docs
- Documented the precedence between session
allowedMethodsand Gr4vy connection configuration. See Embedded Checkout.
2026-06-20 — Cancel a checkout session
API
DELETE /api/v1/checkout/sessions/{id}now cancels an in-flight session (previously a no-op). It is idempotent, marks the sessioncancelled, and re-opens an associated cart still incheckoutstatus (never a terminalconvertedcart). A completed session returns422 already_completed; an unknown session returns404.
SDK
@usethrottle/checkout-sdk. Newcheckout.cancelSession(sessionId)method.
Docs
- Clarified the session→cart lifecycle: creating a session does not move the cart out of
open; the cart only becomesconvertedwhen the order is created at session completion. See Embedded Checkout.
2026-06-20 — Canonical cart address + typed cart errors
API
- Canonical cart address.
PATCH /api/v1/carts/{id}now validatesshippingAddress/billingAddressat write time against one canonical shape (CartAddress: requiredaddressLine1,city,countryCode). Non-canonical keys (line1,state,country,zip) are now rejected with avalidation_errornaming the camelCase replacement, instead of being stored verbatim and failing later at checkout withaddress_required.
SDK
@usethrottle/cart. Exports the canonicalCartAddresstype (used bycarts.updateand the cart response) and two typed lifecycle errors —CartNotOpenError(409cart_not_open) andCartNotFoundError(404). Both extendThrottleApiError, so existing checks keep working. See Errors.
Docs
- Clarified that selecting a shipping method is a single atomic call returning the full recomputed cart, and that the cart (not a client-side copy) is the source of truth for the selected method and totals. See Cart API.
2026-06-20 — Abandoned-carts read APIs
API
- New endpoints.
GET /api/v1/abandoned-carts(cursor-paginated; each row carriescustomer,total,itemCount,abandonedAt, andrecoveryStatus) andGET /api/v1/abandoned-carts/summary(abandonedCount,abandonedValue,recoveryEmailsSentover a trailing window). Both require thecarts:readscope. Available in@usethrottle/api-client. See API reference.
2026-06-20 — Richer cart.abandoned webhook payload
Webhooks
- Enriched payload. The
cart.abandonedoutbound event now carries the full recovery context indata:customer(id,email,firstName; ornullfor anonymous carts),lineItems,currency, totals (subtotal,taxTotal,shippingTotal,discountTotal,total),itemCount, and arecoveryUrl. This lets ESP integrations (e.g. Klaviyo) drive a recovery flow from the single webhook with no follow-up API call. See Webhooks. - Backward compatible. The change is purely additive — only
cartIdandsequenceare guaranteed, so existing consumers are unaffected. The envelopeversionstays"1". - Typed.
@usethrottle/webhook-typesnow types the enrichedCartAbandonedData(new fields are optional).recoveryUrlis populated from the app'scartRecoveryUrlTemplatewhen set, otherwisenull.
Embed config
- Per-app abandonment threshold.
PUT /api/v1/embed-confignow acceptscartAbandonmentThresholdMinutes(andGETreturns it): the minutes of inactivity before an open/checkout cart is treated as abandoned by the sweep. Range15–129600(90 days). Passnullto clear; when unset, the platform default of1440(24h) applies. The value is per application and per environment. See API reference.
2026-05-11 — Team management & per-app roles
Workspace invitations
- Two-tier role model. Workspaces now carry three roles:
owner,workspace_admin, andmember. Members get explicit per-application roles fromadmin,developer,finance, orviewer. See Team management and Permissions. - New invitations + members endpoints.
POST /api/v1/workspaces/:workspaceId/invites,.../invites/:id/resend,.../invites/:id/revoke,GET .../members,GET .../members/me,PATCH .../members/:memberId,DELETE .../members/:memberId,DELETE .../members/:memberId/applications/:applicationId. - Strict email match on accept.
POST /api/v1/invites/acceptnow requires the caller's Clerk verified primary email to match the invite token'semailclaim. Mismatch returns403 invite/email_mismatch. - Permission introspection.
GET /api/v1/auth/permissionsreturns the caller's effectiveworkspaceRoleandappRoleplus the full static catalog. Use it to drive UI gating. - Auth context fields. Server-side handlers now see
auth.workspaceRole,auth.appRole, andauth.workspaceMemberIdon Clerk-authenticated requests. Legacyauth.rolefield preserved.
Emails
- Four new templates seeded by
@platform/emails:system.team_invite_resent,system.team_invite_accepted,system.team_access_revoked,system.team_role_changed.
Legacy compatibility
POST /api/v1/merchants/me/invitesand its/resend,/revokesiblings continue to work — they delegate to the new team-service. Legacy clients sending{ email, role: 'admin' }still receiverole: 'admin'in the response envelope.
2026-05-04 — Collect flags, billing, metadata propagation
Embedded checkout
- Collect flags shipped.
POST /api/v1/checkout/sessionsaccepts a newcollect: { shippingAddress: boolean; billingAddress: boolean }object on the request body. Defaults match historical behaviour (shippingAddress: true,billingAddress: false). See Collection Flags. - Billing address is now first-class.
POST /api/v1/checkout-sessions/:id/completeaccepts newbillingAddress(same shape asshippingAddress) andbillingSameAsShipping: boolean(defaultfalse). Required whencollect.billingAddressis true on the session. step: 'billing'postMessage event. The unified/sflow now emits an additionalthrottle.step.changedevent withstep: 'billing'when the buyer reaches the billing form.fieldextras onaddress_required422 responses. Validation errors at/completenow include afieldkey (e.g."billingAddress"or"shippingAddress") so the iframe and API integrators can route the error to the right form section.- Pay button auto-disables until collect-flagged fields are complete. Parent-set
submitDisabledstill composes additively — see Parent Controls. ?mode=payment-onlydeprecated. Still respected client-side for one release. New integrators should setcollect: { shippingAddress: false, billingAddress: false }instead.
Discounts
- Session-create discountCode.
POST /api/v1/checkout/sessionsaccepts a newdiscountCode: stringfield. Validated synchronously; invalid codes return422 discount_invalid. See Discounts.
Metadata + webhooks
- Session metadata propagates to orders. User-attached
metadataon a checkout session is merged into the order at conversion with precedencecart < session < {customerEmail}. - Reserved keys are stripped server-side.
recurring,customer_prefill,mode,amount,currency, andexternalCartIdare removed from session metadata before persistence; use top-level request fields instead. - Session metadata caps. 50 keys / 10KB serialized. Over-size payloads return
422 metadata_too_large. - Webhook payloads now carry user metadata.
order.created,payment.captured, andsubscription.createdinclude the mergeddata.metadatabag. See Metadata.