The customer portal can be enabled from the Foxy admin at admin.foxy.io. Navigate to the Settings > Customer Portal section where you can configure the portal for your store.
You need to specify an allowlist of domains where the portal is able to be utilised. Note that all domains have to begin with https://, and when adding a primary domain, we recommend adding both the domain with and without the www. subdomain (for example, https://mydomain.com and https://www.mydomain.com). If you're adding a subdomain already (like https://customer.mydomain.com) then that doesn't need a www. prefixed version.
You can also configure additional options for the portal:
You can see an example of our customer portal element on our website here.
A default install will look like this, noting that you need to update the YOUR_FOXY_STORE.foxycart.com to your correct Foxy store subdomain (or custom subdomain if configured):
<foxy-customer-portal base="https://YOUR_FOXY_STORE.foxycart.com/s/customer/"> </foxy-customer-portal> <script type="module"> import 'https://cdn-js.foxy.io/elements@1/foxy-customer-portal.js'; const I18nElement = customElements.get('foxy-i18n'); const i18nBase = 'https://cdn-js.foxy.io/elements@1/translations'; I18nElement.onResourceFetch((ns, lang) => fetch(`${i18nBase}/${ns}/${lang}.json`)); </script>
The CDN url's for the customer portal is set to the stable release and will update according to the @1 release tag. If you want to try the bleeding-edge beta releases, you can switch the URL's to use elements@beta instead of elements@1, but we don't recommend that for production situations.
If you are customising your instance of the customer portal such as adding custom elements or translations, we would recommend locking to a specific version of the portal instead of using the @1 release tag.
To do that, you would specify the specific release instead of @1, for example @1.28.0 in the two URL's from the code above. You can confirm the latest releases by viewing the releases on the Elements GitHub repository here, and also subscribe to that repository to get updates as they're released
If you really want to, it's also possible to use third-party CDN's, such as jsdeliver, though we recommend our own unless you have a compelling reason:
https://cdn.jsdelivr.net/npm/@foxy.io/elements@1/dist/cdn/foxy-customer-portal.js https://cdn.jsdelivr.net/npm/@foxy.io/elements@1/dist/cdn/translations
If you want to customise the styles of your portal to better match your branding, you can do that by including a style block after the script tag shown above. You can customize your styles at https://demo.vaadin.com/lumo-editor/.
The output from the lumo editor will look like this:
<custom-styles> <style> html { --lumo-border-radius: 0.5em; --lumo-shade-5pct: rgba(41, 41, 41, 0.05); --lumo-shade-10pct: rgba(41, 41, 41, 0.1); --lumo-shade-20pct: rgba(41, 41, 41, 0.2); --lumo-shade-30pct: rgba(41, 41, 41, 0.3); --lumo-shade-40pct: rgba(41, 41, 41, 0.4); --lumo-shade-50pct: rgba(41, 41, 41, 0.5); --lumo-shade-60pct: rgba(41, 41, 41, 0.6); --lumo-shade-70pct: rgba(41, 41, 41, 0.7); --lumo-shade-80pct: rgba(41, 41, 41, 0.8); --lumo-shade-90pct: rgba(41, 41, 41, 0.9); --lumo-shade: hsl(214, 0%, 16%); --lumo-primary-text-color: rgb(83, 39, 94); --lumo-primary-color-50pct: rgba(83, 39, 94, 0.5); --lumo-primary-color-10pct: rgba(83, 39, 94, 0.1); --lumo-primary-color: #53275E; --lumo-body-text-color: hsl(214, 0%, 16%); --lumo-secondary-text-color: hsl(214, 0%, 42%); --lumo-tertiary-text-color: rgba(87, 87, 87, 0.5); --lumo-disabled-text-color: rgba(173, 173, 173, 0.3); --lumo-header-text-color: hsl(214, 0%, 16%); } </style> </custom-styles>
Note that dark mode is not currently supported by the customer portal, and if you use the “advanced” options on the “typography” page, our elements don't utilise the “3X large” size.
If you want to include the styles in the head of your page, you can do that by changing the html style declaration to instead be targeting :root.
Languages are handled separately for the customer portal than for the cart/checkout/receipt, and so aren't managed via the Foxy administration. Instead, you can specify a lang attribute on the foxy-customer-portal HTML element. We currently support the following language codes: en, es, de, pl, zh-HK, se, nl, fr. As an example, to set your customer portal to French, it would look like this (with your store domain of course):
<foxy-customer-portal base="https://YOUR_FOXY_STORE.foxycart.com/s/customer/" lang="fr"></foxy-customer-portal>
If you need to adjust some individual language strings within your portal installation beyond our default strings, you can do that with some customisations to your portal page. There are two changes that need to be made, one to the HTML element, and then adding some additional javascript to define your customisations.
To the foxy-customer-portal HTML element, you will need to define a namespace ns element, with a value of my-portal customer-portal. (You can specify a different value than my-portal if you want, you'll just need to remember that in the next portion for the javascript.) That would make the HTML tag look like this:
<foxy-customer-portal base="https://YOUR_FOXY_STORE.foxycart.com/s/customer/" ns="my-portal customer-portal"></foxy-customer-portal>
Then in your javascript, you define lines for each language string you want to customise, specifically after this line in the existing javascript from the installation instructions above:
I18nElement.onResourceFetch((ns, lang) => fetch(`${i18nBase}/${ns}/${lang}.json`));
The javascript line to customise a language string takes the following format:
I18nElement.i18next.addResource('en', 'my-portal', 'STRING_IDENTIFIER', 'My New String');
en) is the language code that contains the string you're updatingmy-portal) matches the custom namespace defined in the ns attributeSTRING_IDENTIFIER) matches the string that you're wanting to update from the language file (details on that below)My New String) defines your own version of the language string that you want to use instead
To find the string identifier, you can reference the language strings for the customer portal element on the Github repository here. After opening the specific language you are replacing a string for, you will find all the strings in a JSON structure. By finding the language string there, you can then create the string identifier to match to it, which begins with customer-portal, followed by the nested object keys and the language string key, separated by periods.
For example, let's replace the “Get temporary password” language string to be “Forgot password?” instead. In the en language file, that string is located in this portion of the document:
"sign-in-form": {
"email": "Email",
"invalid_credential_error": "Incorrect email or password. Please check your credentials and try again.",
"password": "Password",
"recover_access": "Get temporary password",
"sign_in": "Sign in",
"sign_in_hint": "Please enter your email and password",
"sign_up": "Create account",
"unknown_error": "An unknown error has occured. Please try again later.",
"v8n_invalid_email": "Invalid email",
"v8n_required": "Required",
"spinner": {
"loading_busy": "Loading",
"loading_error": "Failed to load"
}
}
That would make the string identifier as customer-portal.sign-in-form.recover_access, and the line of javascript added into the customer portal page would look like this:
I18nElement.i18next.addResource('en', 'my-portal', 'customer-portal.sign-in-form.recover_access', 'Forgot Password?');
Note that some language strings are nested in multiple objects, so ensure you include each nested key to reach the desired language string in the identifier you use. As an example, the “Loading” string above would be customer-portal.sign-in-form.spinner.loading_busy.
You can include as many language customisations as you need, but each different string is a distinct line of javascript.
You can see additional demos and examples at elements.foxy.dev for configuration options and demo code.
Note: This functionality may become native in the future. For the time being, SSO between the portal and the checkout requires just a bit of extra setup.
If you're using SSO elsewhere in your site, be aware that enabling it in the portal bypasses your own endpoint. If you're only doing authentication, this is generally not a problem, but if you're doing other things with your SSO endpoint (like checking inventory, verifying cart contents, etc.), this will bypass that.
This section is for if you're not using SSO elsewhere.
To ensure your customers don't need to login to the checkout after they've already logged into the portal, you can set up Foxy's SSO functionality following the steps below. If you're already using SSO, you'll want to approach this in a different way, depending on your other systems.
When you do a GET /s/customer?sso=true (which happens by default if you're using the normal portal functionality), it includes the ._links[“fx:checkout”].ref value, which is a URI that will load the checkout with your customer already logged in, and sets a cookie named fx.customer.sso. From there, you'll need to do the following:
sso.customer_id=0). (You can use an online tool like CyberChef to generate a SHA1 hash of something like 0|1893456000|YOUR_SECRET_HERE, replacing YOUR_SECRET_HERE with the store secret you copied earlier.) Using that tool, you'll then copy the Output value to use as your fc_auth_token value.<foxy-customer-portal /> element) with the following code. Replace YOUR_FOXY_DOMAIN with your store domain (like mystore.foxycart.com or secure.mystore.com), and the fc_auth_token=REPLACE_THIS bit with the SSO URL token from the above step. (NOTE: The below code includes the doctype, html, head, and body tags. If you're inserting this into an existing template, you just want to grab the <script /> block, and make sure the page is set to not be indexed by search engines. Also try to keep the page as small as possible - removing any extra CSS or javascript files you may be loading, to help the page load as quick as possible.) <!doctype html> <html class="no-js" lang=""> <head> <meta charset="utf-8"> <title>SSO Redirect</title> <meta name="ROBOTS" CONTENT="NOINDEX, NOFOLLOW"> <script> function parseJWT(token) { let base64Url = token.split('.')[1]; let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); let jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); return JSON.parse(jsonPayload); } function getSSOUrl() { // Look for a cookie var value = "; " + document.cookie; var parts = value.split("; " + "fx.customer.sso" + "="); if (parts.length == 2) return decodeURIComponent(parts.pop().split(";").shift()); // Look for localstorage try { let sessionStore = JSON.parse(localStorage.getItem("session")); if (sessionStore && sessionStore.hasOwnProperty("jwt")) { let sessionData = parseJWT(sessionStore.jwt); let expires = Math.floor(new Date(sessionStore.date_created).getTime() / 1000) + sessionStore.expires_in; if ( expires > Math.floor(Date.now() / 1000) && sessionData.hasOwnProperty("customer_id") && sessionStore.hasOwnProperty("sso") ) { return sessionStore.sso; } } } catch (error) { console.log("Error trying to get SSO URL:", error); } return false; } function getUrlParameter(name) { return new URLSearchParams(location.search).get('fcsid'); } if (getSSOUrl()) { window.location.replace(getSSOUrl() + "&fcsid=" + getUrlParameter("fcsid")); } else { window.location.replace("https://YOUR_FOXY_DOMAIN/checkout?fc_customer_id=0×tamp=1893456000&fc_auth_token=REPLACE_THIS&fcsid=" + getUrlParameter("fcsid")); } </script> </head> <body></body> </html>
If you've already got SSO enabled and configured on your Foxy store, you'll need to handle things separately. We can't give step-by-step instructions, as there are countless ways to handle SSO, but here are the general ideas:
POST /s/customer/authenticate request as documented below with their entered username + password. Set a localstorage item named session with a stringified version of the JSON from the successful response. Note that this approach requires the Foxy password to be synced with your own auth system.An often-requested feature (that we will likely add as a native feature, but it's not available yet) is the idea of “loyalty points” that can be redeemed or exchanged for discounts. Using the API and the customer portal functionality described on this page, you can whip something up. The idea is, roughly:
attributes for the transaction and look for an attribute named Loyalty_Points_Applied_On. If it exists, you've already applied the points for this transaction, so exit.Loyalty_Points attribute exists, update the value accordingly. On success, add the Loyalty_Points_Applied_On attribute to the transaction, so you don't re-apply points if the webhook fires again. (If the Loyalty_Points attribute doesn't exist, create it and make sure the visibility is public, so it will be returned in the customer portal front-end.)
That gets you keeping track of points, and the public visibility ensures that your customers can see their points value when they login.
To allow customers to redeem their points, you'd need to add some logic to your portal and your backend custom code. It'd look like this, roughly:
authenticate action to validate the request.)This is obviously a bit of work, and requires some more advanced programming knowledge. But hopefully it helps with some ideas of how you can use the Foxy backend API, paired with the front-end javascript portal API, to create more advanced functionality.
Note that using the new /s/customer endpoint is a more advanced approach. If you're not sure if you actually need to use this, please contact us to discuss your needs, as you may want to just use our “ready to go” portal front-end detailed above instead of building your own.
The new /s/customer endpoint is a bit different than the other public-facing endpoints in Foxy. Some notes:
GET requests are for retrieving (and not modifying) data. POST is to create something new. PATCH is for modifying existing things.POST and PATCH, you should submit a JSON object in the body of the request. “FOXY-API-VERSION”: “1” with your requests200 status code indicates a successful request/response, and will have a JSON payload.{ "type": "authentication", "code": "422", "message": "`password` not defined or invalid." }
POST /s/customer/authenticate
emailpasswordfetch('https://foxy-demo.foxycart.com/s/customer/authenticate', { method: 'POST', headers: { 'Content-Type': 'application/json', "FOXY-API-VERSION": "1" }, body: JSON.stringify({ email: 'demo@example.com', password: 'demo12345' }) }) .then(response => response.json()) .then(data => console.log(data));
{ "expires_in": 172800, "session_token": "cf9a7097-bc93-4ff0-8e47-986fffb4c380", "jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdG9yZV9pZCI6MTIzNDUsImN1c3RvbWVyX2lkIjo2Nzg5MCwiY3VzdG9tZXJfZW1haWwiOiJmYW5jeXBhbnRzeUBnbWFpbC50bGQiLCJpYXQiOjE1Nzg0NzA3MDYsImV4cCI6MTU3ODQ3NjEwNn0.r7gimkYED0GlcT0CkrxPIYmMoux5pE9VFdxJ9D4Le-XClPB1UM0-0-A1gWeQqkpUIfADsm_jPAdAmIWaThvCi_ItUSHTtQD9RWZQMzyHvLbQf5KwyjDMlim8G7CtCl0LObpbBWuXDomqytlyxlhA1ynafTYJ5DuB4chBAX5U4suvTIAIrwi2lGQsXMSPmjzUXhceuX9UacWPpP6j_dGeaECEW8vlS4ImefGRcOeG8sjJiRkYsQOeW-u7zCUsKAAYoE7iuJ0FGlYvWxDnpUkU7zMAGAVfkK_SpNOl4p1nG9SEMLZ6iSM_-SquCKvUB0Rpf6iC4ioG_KZQjEEvD0g0Q_bqpeCdY9rkgfCud2O-oihLs_l0YjGqCcqsOFgBJLxZUuWjxIXQ5NUqBrCflmCg102N1PfMPo_qWHBJ_-RC0bbX6dOql3MLWTn4XxWArCNdHvBGwEfjv0i7Nps-gootMftYdo6TqNreqGY0cHpR-FdNYQ8W0dfR7hfFiorhzyDgGnduWZ7KlwTruf3ogo3O0XaHc_d5SdFqnirus3lSrpz8U9LgbRe3a52D_OJImkj1fH-sz3UgKZBx5rV8xDgW4dPJKskNIWFBPh17LR37U0NiIlxVTQV87gUTEKp8maOukEg2hAJklqpVzK2eZaEnLivBF20CMemwG4358UpFYMk" }
expires_in is an integer value, and matches the session lifetime as configured in Foxy (in seconds).jwt uses HS256 (shared secret) signing. This is optional functionality. More details at JWT.io. Note that we do have keypair-based JWT capabilities, but because the returned token is so huge, and the use case is often cookies, we feel that's less helpful. If you'd like to use a public/private keypair instead of shared secret, however, please let us know.session_token value can be used for authenticating your following requests as a Bearer token in an Authorization header, like Authorization: Bearer cf9a7097-bc93-4ff0-8e47-986fffb4c380
GET /s/customer
zoom: Zoomable resources are:default_billing_addressdefault_shipping_addressdefault_payment_methodsso: An SSO URI will be returned as _links[“fx:checkout”]. NOTE: This is optional. See notes about SSO below.{ "_embedded": { "fx:attributes": [ { "name": "Loyalty Points Balance", "value": "100", "visibility": "public", "date_created": "2019-07-09T00:41:44-07:00", "date_modified": "2019-07-09T00:41:44-07:00" } ], "fx:default_billing_address": { "address_name": "Default Billing Address", "first_name": "foo", "last_name": "bar", "company": "aloha", "address1": "4612 avenue b", "address2": "unit a", "city": "Mullberry", "region": "TX", "postal_code": "12345", "country": "US", "phone": "87654321", "is_default_billing": true, "is_default_shipping": false, "date_created": "2018-10-18T17:29:20-07:00", "date_modified": "2020-01-27T00:26:58-08:00" }, "fx:default_shipping_address": { "address_name": "Me", "first_name": "foo", "last_name": "baz3", "company": "aloha 10", "address1": "4612 avenue b", "address2": "unit a", "city": "Mullberry", "region": "TX", "postal_code": "12345", "country": "US", "phone": "87654321", "is_default_billing": false, "is_default_shipping": true, "date_created": "2018-10-18T17:29:20-07:00", "date_modified": "2020-01-27T00:26:58-08:00" }, "fx:default_payment_method": { "save_cc": false, "cc_type": "Visa", "cc_number_masked": "", "cc_exp_month": "", "cc_exp_year": "", "date_created": "2018-10-18T17:29:20-07:00", "date_modified": "2020-01-27T00:26:58-08:00" }, "fx:transactions": [ { "_links": { "fx:receipt": { "href": "https://demo.foxy.tld/receipt?id=abc123", "title": "This Receipt", "type": "text/html" } }, "_embedded": { "fx:attributes": [ { "_links": {}, "name": "cart attribute 1", "value": "value 1", "visibility": "public", "date_created": "2020-01-07T16:54:34-08:00", "date_modified": "2020-01-07T16:54:34-08:00" }, { "_links": {}, "name": "cart attribute 2", "value": "value 2", "visibility": "public", "date_created": "2020-01-07T16:54:34-08:00", "date_modified": "2020-01-07T16:54:34-08:00" } ] }, "id": 3881585, "display_id": 3881585, "is_test": true, "hide_transaction": false, "data_is_fed": false, "transaction_date": "2020-01-08T10:55:29+10:00", "locale_code": "en_US", "customer_first_name": "Grace", "customer_last_name": "Hopper", "customer_tax_id": "1234567890", "customer_email": "foobar@foxy.tld", "customer_ip": "", "ip_country": null, "user_agent": "", "total_item_price": 209.99, "total_tax": 16.5, "total_shipping": 0, "total_future_shipping": 0, "total_order": 216.5, "status": "captured", "date_created": "2020-01-07T16:45:14-08:00", "date_modified": "2020-01-07T16:58:16-08:00" } ], "fx:subscriptions": [ { "_links": { "self": { "href": "https://demo.foxy.tld/s/customer/subscriptions/2258", "title": "This Subscription" }, "fx:sub_token_url": { "href": "https://demo.foxy.tld/cart?sub_token=xyz789", "title": "This Sub Token", "type": "text/html" } }, "start_date": "2019-03-18T00:00:00-07:00", "next_transaction_date": "2019-04-18T00:00:00-07:00", "end_date": null, "frequency": "1m", "error_message": "", "past_due_amount": 0, "first_failed_transaction_date": null, "is_active": true, "date_created": "2019-03-18T00:02:46-07:00", "date_modified": "2019-03-18T00:02:46-07:00", "_embedded": { "template_config": { "allow_next_date_modification": true } } } ] }, "_links": { "fx:checkout": { "href": "https://demo.foxy.tld/checkout?fc_customer_id=12345×tamp=1580209218&fc_auth_token=poiuytrewq", "title": "Checkout" } }, "date_created": "2018-10-18T17:29:20-07:00", "date_modified": "2020-01-27T00:26:58-08:00", "email": "foobar@foxy.tld", "first_name": "foo", "id": 52008, "is_anonymous": false, "last_login_date": "2020-01-09T23:15:32-08:00", "last_name": "bar", "tax_id": "1234567890" }
PATCH /s/customer
first_namelast_name emailtax_idpassword: A new password. Allows for password resets. Must be 8 characters. Note that if you want to enforce password complexity requirements, you must do that in your UI.password_old: Required if password is passed in. Must match the old password, or the temporary pasword generated from a password reset request (sent via email)._embedded: Can contain the following objects. Note that you must still zoom any embedded resources to get them back in the response.fx:default_billing_address: See https://api.foxycart.com/rels/default_billing_addressfx:default_shipping_address: See https://api.foxycart.com/rels/default_shipping_addressfx:default_payment_method: See https://api.foxycart.com/rels/default_payment_method. Note that cc_number is not updatable via the customer portal.GET response to /s/customer.
GET /s/customer/transactions
{ "_links": { "self": { "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=0&order=transaction_date+desc", "title": "This Collection" }, "first": { "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=0&order=transaction_date+desc", "title": "First Page of this Collection" }, "prev": { "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=0&order=transaction_date+desc", "title": "Previous Page of this Collection" }, "next": { "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=2&order=transaction_date+desc", "title": "Next Page of this Collection" }, "last": { "href": "https://demo.foxy.tld/s/customer/transactions?limit=2&offset=52&order=transaction_date+desc", "title": "Last Page of this Collection" } }, "_embedded": { "fx:transactions": [ { "_links": { "self": { "href": "https://demo.foxy.tld/s/customer/transactions/3881585", "title": "This Transaction" }, "fx:receipt": { "href": "https://demo.foxy.tld/receipt?id=abc123", "title": "This Receipt", "type": "text/html" } }, "_embedded": { "fx:attributes": [ { "_links": { "self": { "href": "https://demo.foxy.tld/s/customer/transaction_attributes/1599", "title": "This transaction attribute" } }, "name": "cart attribute 1", "value": "value 1", "visibility": "public", "date_created": "2020-01-07T16:54:34-08:00", "date_modified": "2020-01-07T16:54:34-08:00" }, { "_links": { "self": { "href": "https://demo.foxy.tld/s/customer/transaction_attributes/1600", "title": "This transaction attribute" } }, "name": "cart attribute 2", "value": "value 2", "visibility": "public", "date_created": "2020-01-07T16:54:34-08:00", "date_modified": "2020-01-07T16:54:34-08:00" } ] }, "id": 3881585, "display_id": 3881585, "is_test": true, "hide_transaction": false, "data_is_fed": false, "transaction_date": "2020-01-08T10:55:29+10:00", "locale_code": "en_US", "customer_first_name": "Grace", "customer_last_name": "Hopper", "customer_tax_id": "1234567890", "customer_email": "foobar@foxy.tld", "customer_ip": "", "ip_country": null, "user_agent": "", "total_item_price": 209.99, "total_tax": 16.5, "total_shipping": 0, "total_future_shipping": 0, "total_order": 216.5, "status": "captured", "date_created": "2020-01-07T16:45:14-08:00", "date_modified": "2020-01-07T16:58:16-08:00" }, { "_links": { "self": { "href": "https://demo.foxy.tld/s/customer/transactions/3876502", "title": "This Transaction" }, "fx:receipt": { "href": "https://demo.foxy.tld/receipt?id=abc123", "title": "This Receipt", "type": "text/html" } }, "_embedded": { "fx:attributes": [ { "_links": { "self": { "href": "https://demo.foxy.tld/s/customer/transaction_attributes/1592", "title": "This transaction attribute" } }, "name": "cart attribute 1", "value": "value 1", "visibility": "public", "date_created": "2019-12-07T00:08:08-08:00", "date_modified": "2019-12-07T00:08:08-08:00" }, { "_links": { "self": { "href": "https://demo.foxy.tld/s/customer/transaction_attributes/1593", "title": "This transaction attribute" } }, "name": "cart attribute 2", "value": "value 2", "visibility": "public", "date_created": "2019-12-07T00:08:08-08:00", "date_modified": "2019-12-07T00:08:08-08:00" } ] }, "id": 3876502, "display_id": 3876502, "is_test": true, "hide_transaction": false, "data_is_fed": false, "transaction_date": "2019-12-07T18:08:15+10:00", "locale_code": "en_US", "customer_first_name": "Grace", "customer_last_name": "Hopper", "customer_tax_id": "1234567890", "customer_email": "foobar@foxy.tld", "customer_ip": "", "ip_country": null, "user_agent": "", "total_item_price": 419.98, "total_tax": 33.82, "total_shipping": 0, "total_future_shipping": 0, "total_order": 443.81, "status": "authorized", "date_created": "2019-12-05T21:58:02-08:00", "date_modified": "2019-12-07T00:10:35-08:00" } ] }, "total_items": 53, "returned_items": 2, "limit": 2, "offset": 0 }
GET /s/customer/subscriptions
_embedded.template_config.allow_next_date_modification value is a boolean that indicates whether or not a subscription's next date can be modified by the customer. Note that this is included per transaction, though the setting at this point is store-wide. In the future, there may be additional template_config options to help inform the UI.{ "_links": { "self": { "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=0&order=next_transaction_date+asc", "title": "This Collection" }, "first": { "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=0&order=next_transaction_date+asc", "title": "First Page of this Collection" }, "prev": { "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=0&order=next_transaction_date+asc", "title": "Previous Page of this Collection" }, "next": { "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=1&order=next_transaction_date+asc", "title": "Next Page of this Collection" }, "last": { "href": "https://demo.foxycart.tld/s/customer/subscriptions?is_active=true&limit=1&offset=21&order=next_transaction_date+asc", "title": "Last Page of this Collection" } }, "_embedded": { "fx:subscriptions": [ { "_links": { "self": { "href": "https://demo.foxycart.tld/s/customer/subscriptions/2258", "title": "This Subscription" }, "fx:sub_token_url": { "href": "https://demo.foxycart.tld/cart?sub_token=abc123", "title": "This Sub Token", "type": "text/html" } }, "start_date": "2019-03-18T00:00:00-07:00", "next_transaction_date": "2019-04-18T00:00:00-07:00", "end_date": null, "frequency": "1m", "error_message": "", "past_due_amount": 0, "first_failed_transaction_date": null, "is_active": true, "date_created": "2019-03-18T00:02:46-07:00", "date_modified": "2019-03-18T00:02:46-07:00", "_embedded": { "template_config": { "allow_next_date_modification": true, "allow_frequency_modification": [ ["1m","2m","3m","6m","1y"] ] } } } ] }, "total_items": 21, "returned_items": 1, "limit": 1, "offset": 0 }
PATCH /s/customer/subscriptions/SUBSCRIPTION_ID
$contains(frequency, “w”) allows [“1w”,“2w”,“3w”,“4w”,“5w”,“6w”,“7w”,“8w”,“9w”,“10w”,“11w”,“12w”], and $contains(frequency, “m”) allows [“1m”,“2m”,“3m”,“6m”,“1y”]. (The transaction_template with items and item_options is zoomed on the transaction, and can be used in your JSONata queries. Only values returned in the allow_frequency_modification node are allowed to be patched, or the request will error._links.self value (seen above).