Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.subtotal.com/llms.txt

Use this file to discover all available pages before exploring further.

The Subtotal Connect Shopify app exposes a small set of JSON endpoints through Shopify’s app proxy. You can call them from any storefront page to build custom UI — for example, a “Link your Walmart account” button on a marketing page, a CTA inside a loyalty widget, or an integration with a third-party storefront framework — without using the theme app block or profile UI extension.
If the theme app block or profile UI extension meets your needs, use those instead — they handle UI, state, and styling for you. The Storefront API is for cases where you need a custom entrypoint or a different layout.

Base path

All endpoints are mounted at /apps/subtotal/* on the merchant’s storefront domain. Fetch them with a relative path:
fetch('/apps/subtotal/retailers')

Authentication

You do not send an API key. Shopify’s app proxy signs every request server-side, so calls only work when they originate from a page on the merchant’s storefront. When a customer is signed in, Shopify automatically appends logged_in_customer_id as a query parameter when proxying the request to the Subtotal Connect backend. Endpoints that operate on a specific customer (/retailers, /connections, /disconnect-connection, /visibility, /link/{linkId}) require the customer to be signed in. If they aren’t, those endpoints return 400 or redirect to the Shopify customer login.

Endpoints

MethodPathPurpose
GET/apps/subtotal/configurationCheck whether the shop has Subtotal Connect configured
GET/apps/subtotal/visibilityCheck visibility rules for the current customer
GET/apps/subtotal/retailersList retailers with the current customer’s connection status
POST/apps/subtotal/connectionsCreate or reuse a connection, return a Subtotal Link URL
POST/apps/subtotal/disconnect-connectionDisconnect an existing connection
GET/apps/subtotal/link/{linkId}One-shot redirect into Subtotal Link for a single retailer

GET /apps/subtotal/configuration

Returns 200 if the shop has configured Subtotal Connect (i.e. a Subtotal API key is stored for the shop). Returns 404 otherwise. Use this to feature-detect before rendering your UI. Response
{ "status": "success" }
Example
async function isSubtotalConnectConfigured() {
  const response = await fetch('/apps/subtotal/configuration');
  return response.ok;
}

GET /apps/subtotal/visibility

Returns the visibility decision for the current customer based on the rules the merchant has configured in the Shopify admin (global allow-list of emails/domains, plus a per-retailer allow-list). Use this if you want your custom entrypoint to respect the same gating as the theme app block. Response
{
  "blockVisible": true,
  "restrictedRetailerIds": []
}
  • blockVisiblefalse when the merchant’s global visibility rules hide Subtotal Connect for this customer.
  • restrictedRetailerIds — IDs the merchant has hidden from this specific customer. Filter these out of any retailer list you render.
Example
async function checkVisibility() {
  const response = await fetch('/apps/subtotal/visibility');
  if (!response.ok) return { blockVisible: true, restrictedRetailerIds: [] };
  return response.json();
}

GET /apps/subtotal/retailers

Returns the list of retailers supported for the shop, with the current customer’s connection (if any) attached to each retailer. Response
{
  "retailers": [
    {
      "retailer_id": "walmart",
      "name": "Walmart",
      "link_url": "https://link.subtotal.com/zklQOnlG",
      "images": {
        "logo": { "light": "...", "dark": "..." },
        "icon": { "light": "...", "dark": "..." }
      },
      "connection": {
        "connection_id": "01K...",
        "retailer_id": "walmart",
        "status": "active"
      }
    }
  ]
}
retailer_id is a lowercase slug (e.g. walmart, amazon, kroger). The link_url path suffix is a short mixed-case alphanumeric linkId (e.g. zklQOnlG) used by /apps/subtotal/link/{linkId}. connection is omitted when the customer has never linked the retailer. status is one of:
StatusMeaningSuggested button text
(no connection)The customer has never linked this retailer”Link Walmart”
initializedConnection created but not yet authenticated”Link Walmart”
disconnectedCustomer disconnected the account”Link Walmart”
revokedRetailer revoked access”Link Walmart”
unauthenticatedPreviously authenticated, needs the customer to sign in again”Login to sync new purchases”
activeAuthenticated and collecting purchases”Disconnect”, “Purchase on Walmart” (link to your brand’s products on the retailer), or hide the button
Treat initialized, disconnected, and revoked the same as “no connection” — show a fresh Link action that starts a new linking flow. Example
async function getRetailers() {
  const response = await fetch('/apps/subtotal/retailers');
  if (!response.ok) throw new Error(`retailers fetch failed: ${response.status}`);
  const data = await response.json();
  return data.retailers;
}

POST /apps/subtotal/connections

Creates (or reuses) a Subtotal connection for the current customer and returns a URL that opens Subtotal Link with the connection pre-bound. Redirect the customer to link_url to start the linking flow. Request body
FieldTypeRequiredDescription
retailerIdstringyesretailer_id from /apps/subtotal/retailers
retailerLinkUrlstringyeslink_url from the same retailer object
redirectUrlstringyesURL Subtotal Link should send the customer back to after they finish (e.g. window.location.href)
connectionIdstringnoExisting connection ID — pass this when reauthenticating an unauthenticated connection
Response
{
  "link_url": "https://link.subtotal.com/zklQOnlG?connection_id=01K...&redirect_url=https%3A%2F%2Fshop.example.com%2Faccount"
}
Example — a Connect button that opens Subtotal Link for a single retailer:
async function startLinkFlow(retailer) {
  const body = {
    retailerId: retailer.retailer_id,
    retailerLinkUrl: retailer.link_url,
    redirectUrl: window.location.href,
  };
  if (retailer.connection) {
    // Reauthenticate an existing connection
    body.connectionId = retailer.connection.connection_id;
  }

  const response = await fetch('/apps/subtotal/connections', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  if (!response.ok) throw new Error(`failed to create connection: ${response.statusText}`);

  const { link_url } = await response.json();
  window.location.href = link_url;
}

POST /apps/subtotal/disconnect-connection

Disconnects an existing connection. After this returns, the connection’s status moves to disconnected and Subtotal stops collecting purchases for it. Request body
FieldTypeRequiredDescription
connectionIdstringyesconnection_id from /apps/subtotal/retailers
Response
{ "success": true }
Example
async function disconnect(connectionId) {
  const response = await fetch('/apps/subtotal/disconnect-connection', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ connectionId }),
  });
  if (!response.ok) throw new Error(`failed to disconnect: ${response.statusText}`);
}

GET /apps/subtotal/link/{linkId}

One-shot redirect endpoint. Resolves the retailer with the given linkId (the last path segment of a retailer’s link_url), creates or finds the customer’s connection, and 302s into Subtotal Link. If the customer isn’t signed in, they’re redirected to Shopify’s customer login first and bounced back here on success. Useful for plain <a href> entrypoints where you don’t want to run any client-side JavaScript. Query parameters
ParamRequiredDescription
redirect_urlnoWhere to send the customer after they finish (defaults to the shop’s homepage)
Example
<a href="/apps/subtotal/link/zklQOnlG?redirect_url=https://shop.example.com/account">
  Link your Walmart account
</a>

Putting it together

A minimal custom Connect button — feature-detect, fetch the customer’s Walmart connection state, and render the right entrypoint for the current status. For linking and reauthenticating, /apps/subtotal/link/{linkId} does the work in one step, so the button can be a plain <a href>. Only disconnect needs a JS click handler.
const configured = await fetch('/apps/subtotal/configuration').then(r => r.ok);
if (!configured) return;

const { retailers } = await fetch('/apps/subtotal/retailers').then(r => r.json());
const walmart = retailers.find(r => r.retailer_id === 'walmart');

const status = walmart.connection?.status;
const linkId = walmart.link_url.split('/').pop();
const redirectUrl = encodeURIComponent(window.location.href);
const slot = document.querySelector('#subtotal-entrypoint');

if (status === 'active') {
  // Already linked — render a Disconnect button
  const btn = document.createElement('button');
  btn.textContent = 'Disconnect Walmart';
  btn.addEventListener('click', async () => {
    await fetch('/apps/subtotal/disconnect-connection', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ connectionId: walmart.connection.connection_id }),
    });
    window.location.reload();
  });
  slot.appendChild(btn);
} else {
  // Not linked (or needs reauth) — a plain link to the convenience redirect
  // covers both cases: it creates a connection if needed, or reuses the
  // existing one for unauthenticated reauth.
  const a = document.createElement('a');
  a.href = `/apps/subtotal/link/${linkId}?redirect_url=${redirectUrl}`;
  a.textContent = status === 'unauthenticated'
    ? 'Login to sync new purchases'
    : 'Link Walmart';
  slot.appendChild(a);
}
If you don’t need to inspect the customer’s current connection status (for example, on a marketing page where you just want a “Link your Walmart account” CTA), skip the /retailers call entirely and hard-code the linkId:
<a href="/apps/subtotal/link/zklQOnlG?redirect_url=https://shop.example.com/account">
  Link your Walmart account
</a>