Skip to main content

Storefront Customer Portal API

Storefront API for customer self-service: pause, cancel, skip, swap products, update billing, and manage subscriptions.

Updated over a month ago

Storefront Customer Portal API

Build custom membership management experiences in your Shopify theme. This API allows customers to manage their subscriptions directly from your storefront using Liquid variables for authentication.


Overview

The Storefront Portal API is designed for theme developers who want to create custom customer dashboards. Unlike the backend Membership Management API, this endpoint uses customer-specific tokens stored in Shopify metafields for authentication.


Endpoint

Property

Value

URL

{store}.myshopify.com/apps/subscribfy-api/store/manage-membership-dashboard

Method

GET or POST

Content-Type

application/x-www-form-urlencoded


Authentication

Authentication uses customer metafields set by Subscribfy. These are accessible in Liquid templates.

Required Parameters

Parameter

Liquid Variable

Description

cid

##{{ customer.id }}

Shopify customer ID

hash

##{{ customer.metafields.exison.exison_hash }}

Subscribfy authentication token

exm

1

Required flag (always set to 1)

Base URL Template (Liquid)

{% assign base_url = shop.url | append: "/apps/subscribfy-api/store/manage-membership-dashboard" %}
{% assign auth_params = "?cid=" | append: customer.id | append: "&hash=" | append: customer.metafields.exison.exison_hash | append: "&exm=1" %}
{% assign api_url = base_url | append: auth_params %}


Customer Metafields Reference

Subscribfy creates the following metafields on customer records:

Namespace: exison

Key

Description

exison_hash

Authentication token for API requests

customer_subscription1

Primary membership/subscription data (JSON)

customer_subscription1_parent

Parent buyer info for gifted memberships

customer_subscription1_gifts

Memberships this customer has gifted to others

customer_subscription1 Structure

Field

Type

Description

name

string

Subscription/membership name

status

string

ACTIVE, PAUSED, or CANCELLED

member_since

string

Membership start date (e.g., "May 28, 2023")

next_billing

string

Next billing date (e.g., "October 28, 2023")

scid

string

Subscribfy contract ID (required for actions)

Liquid Example: Display Membership Status

{% assign membership = customer.metafields.exison.customer_subscription1 %}
{% if membership %}
  <div class="membership-card">
    <h3>##{{ membership.name }}</h3>
    <p>Status: <strong>##{{ membership.status }}</strong></p>
    <p>Member since: ##{{ membership.member_since }}</p>
    {% if membership.status == "ACTIVE" %}
      <p>Next billing: ##{{ membership.next_billing }}</p>
    {% endif %}
  </div>
{% endif %}


Available Actions

Action

Description

Additional Parameters

getStoreCreditHistory

Get customer's store credit transactions

None (no scid required)

updatePaymentContent

Send payment method update email

scid

updatePauseContent

Pause subscription

scid, period (1-3)

updateCancelContent

Cancel subscription

scid, reason

updateReactivateContent

Reactivate subscription

scid

updateSkipContent

Skip next order (pause 1 month)

scid

updateShipNowContent

Trigger immediate shipment

scid, info[update_billing_date]

updateChargeNowContent

Trigger immediate charge

scid, info[update_billing_date]

updateItemContent

Update item quantity

scid, info[line], info[qty]

removeItemContent

Remove item from subscription

scid, info[line]

swapProduct

Swap product in subscription

scid, info[line], info[product_id]

ShowSwapContent

Get available products for swap

scid, info[productAction]

updateRefundContent

Request refund

scid, order_gid (optional)


Response Format

Response Codes

Code

Meaning

Description

result: 1

Success

Action completed successfully

result: -1

Validation Error

Invalid customer, contract not found, or invalid payment method

result: -2

Unauthorized

Customer not allowed to perform this action

result: 9

Rate Limited

Too many requests (wait 15 seconds)


Examples

Get Store Credit History

POST {store}/apps/subscribfy-api/store/manage-membership-dashboard
  ?cid={customer.id}
  &hash={customer.metafields.exison.exison_hash}
  &exm=1
  &action=getStoreCreditHistory

Response:

{
  "result": 1,
  "data": [
    {
      "body": "You've earned 29.00 Store Credits!",
      "value": 29,
      "currency": "USD",
      "type": "Membership",
      "status": "Active",
      "for_order_gid": 5412270689000,
      "created_at_unixtimestamp": 1735473052,
      "created_at_human": "2024-12-29"
    },
    {
      "body": "Discount Redemption",
      "value": -10,
      "currency": "USD",
      "type": "redeem",
      "status": "Active",
      "for_order_gid": 655554481300,
      "created_at_unixtimestamp": 1717223080,
      "created_at_human": "2024-06-01"
    }
  ]
}

Pause Subscription

POST {store}/apps/subscribfy-api/store/manage-membership-dashboard
  ?cid={customer.id}
  &hash={hash}
  &exm=1
  &scid={contract_id}
  &action=updatePauseContent
  &period=2

Response:

{"result": 1}

Cancel Subscription

POST {store}/apps/subscribfy-api/store/manage-membership-dashboard
  ?cid={customer.id}
  &hash={hash}
  &exm=1
  &scid={contract_id}
  &action=updateCancelContent
  &reason=Too%20expensive

Response:

{"result": 1}

Update Payment Method

Sends an email to the customer with a link to update their payment method.

POST {store}/apps/subscribfy-api/store/manage-membership-dashboard
  ?cid={customer.id}
  &hash={hash}
  &exm=1
  &scid={contract_id}
  &action=updatePaymentContent

Response:

{"result": 1}

Reactivate Subscription

POST {store}/apps/subscribfy-api/store/manage-membership-dashboard
  ?cid={customer.id}
  &hash={hash}
  &exm=1
  &scid={contract_id}
  &action=updateReactivateContent

Response:

{"result": 1}


JavaScript Implementation

Basic API Call

<script>
const SUBSCRIBFY_API = {
  baseUrl: '##{{ shop.url }}/apps/subscribfy-api/store/manage-membership-dashboard',
  cid: '##{{ customer.id }}',
  hash: '##{{ customer.metafields.exison.exison_hash }}',
  scid: '##{{ customer.metafields.exison.customer_subscription1.scid }}',  async call(action, params = {}) {
    const formData = new URLSearchParams({
      cid: this.cid,
      hash: this.hash,
      exm: '1',
      scid: this.scid,
      action: action,
      ...params
    });    const response = await fetch(this.baseUrl, {
      method: 'POST',
      body: formData
    });    return response.json();
  },  async pause(months) {
    return this.call('updatePauseContent', { period: months });
  },  async cancel(reason) {
    return this.call('updateCancelContent', { reason: reason });
  },  async reactivate() {
    return this.call('updateReactivateContent');
  },  async updatePayment() {
    return this.call('updatePaymentContent');
  },  async getStoreCreditHistory() {
    const formData = new URLSearchParams({
      cid: this.cid,
      hash: this.hash,
      exm: '1',
      action: 'getStoreCreditHistory'
    });    const response = await fetch(this.baseUrl, {
      method: 'POST',
      body: formData
    });    return response.json();
  }
};// Usage examples
document.getElementById('pause-btn').addEventListener('click', async () => {
  const result = await SUBSCRIBFY_API.pause(1);
  if (result.result === 1) {
    alert('Subscription paused successfully!');
    location.reload();
  } else if (result.result === 9) {
    alert('Please wait before trying again.');
  } else {
    alert('Unable to pause subscription.');
  }
});
</script>


Liquid Form Examples

Pause Form

<form method="POST" action="##{{ shop.url }}/apps/subscribfy-api/store/manage-membership-dashboard">
  <input type="hidden" name="cid" value="##{{ customer.id }}">
  <input type="hidden" name="hash" value="##{{ customer.metafields.exison.exison_hash }}">
  <input type="hidden" name="exm" value="1">
  <input type="hidden" name="scid" value="##{{ customer.metafields.exison.customer_subscription1.scid }}">
  <input type="hidden" name="action" value="updatePauseContent">  <label>Pause for:</label>
  <select name="period">
    <option value="1">1 month</option>
    <option value="2">2 months</option>
    <option value="3">3 months</option>
  </select>  <button type="submit">Pause Membership</button>
</form>

Cancel Form

<form method="POST" action="##{{ shop.url }}/apps/subscribfy-api/store/manage-membership-dashboard">
  <input type="hidden" name="cid" value="##{{ customer.id }}">
  <input type="hidden" name="hash" value="##{{ customer.metafields.exison.exison_hash }}">
  <input type="hidden" name="exm" value="1">
  <input type="hidden" name="scid" value="##{{ customer.metafields.exison.customer_subscription1.scid }}">
  <input type="hidden" name="action" value="updateCancelContent">  <label>Reason for cancellation:</label>
  <select name="reason">
    <option value="Too expensive">Too expensive</option>
    <option value="Not using enough">Not using enough</option>
    <option value="Found alternative">Found alternative</option>
    <option value="Other">Other</option>
  </select>  <button type="submit">Cancel Membership</button>
</form>


Gifted Memberships

When a membership is gifted, both the gift giver (parent) and recipient have access to certain actions.

Action Permissions

Action

Parent (Gift Giver)

Recipient

Cancel

Yes

Yes

Pause

Yes

No

Reactivate

Yes

No

Update Payment

No

No

Checking Gift Status in Liquid

{% assign parent = customer.metafields.exison.customer_subscription1_parent %}
{% assign gifts = customer.metafields.exison.customer_subscription1_gifts %}{% if parent %}
  <p>This membership was gifted to you by ##{{ parent.name }}.</p>
{% endif %}{% if gifts %}
  <h4>Memberships you've gifted:</h4>
  <ul>
    {% for gift in gifts %}
      <li>##{{ gift.recipient_name }} - ##{{ gift.status }}</li>
    {% endfor %}
  </ul>
{% endif %}


Rate Limits

  • State-changing actions (pause, cancel, reactivate): 15 second cooldown

  • Item/product actions (update quantity, swap): 2 second cooldown

  • Read actions (getStoreCreditHistory): No limit

When rate limited, the API returns {"result": 9}. Display a friendly message asking the customer to wait.


Best Practices

  • Check metafield existence - Always verify metafields exist before rendering forms

  • Handle all response codes - Provide clear feedback for success, errors, and rate limits

  • Use AJAX for better UX - Avoid full page reloads with JavaScript fetch calls

  • Confirm destructive actions - Add confirmation dialogs for cancel/pause actions

  • Cache credit history - Store credit history changes infrequently, cache responses

  • Respect gift permissions - Hide unavailable actions for gift recipients


Default Dashboard

Subscribfy provides a built-in customer dashboard that handles all these actions automatically. If you don't need a custom implementation, use the default dashboard link:

<a href="##{{ shop.url }}/apps/subscribfy-api/store/manage-membership-dashboard?cid=##{{ customer.id }}&hash=##{{ customer.metafields.exison.exison_hash }}&exm=1">
  Manage My Membership
</a>


Questions? Contact support@subscribfy.com

Did this answer your question?