Subscribfy
Subscriptions

Storefront Customer Portal API

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

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

PropertyValue
URL{store}.myshopify.com/apps/subscribfy-api/store/manage-membership-dashboard
MethodGET or POST
Content-Typeapplication/x-www-form-urlencoded

Authentication

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

Required Parameters

ParameterLiquid VariableDescription
cid{{ customer.id }}Shopify customer ID
hash{{ customer.metafields.exison.exison_hash }}Subscribfy authentication token
exm1Required 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

KeyDescription
exison_hashAuthentication token for API requests
customer_subscription1Primary membership/subscription data (JSON)
customer_subscription1_parentParent buyer info for gifted memberships
customer_subscription1_giftsMemberships this customer has gifted to others

customer_subscription1 Structure

FieldTypeDescription
namestringSubscription/membership name
statusstringACTIVE, PAUSED, or CANCELLED
member_sincestringMembership start date (e.g., "May 28, 2023")
next_billingstringNext billing date (e.g., "October 28, 2023")
scidstringSubscribfy contract ID (required for actions)

Liquid Example: Display Membership Status

{% assign membership = customer.metafields.exison.customer_subscription1 %}
{% if membership %}
  <h2>{{ membership.name }}</h2>
  <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 %}
{% endif %}

Available Actions

ActionDescriptionAdditional Parameters
getStoreCreditHistoryGet customer's store credit transactionsNone (no scid required)
updatePaymentContentSend payment method update emailscid
updatePauseContentPause subscriptionscid, period (1-3)
updateCancelContentCancel subscriptionscid, reason
updateReactivateContentReactivate subscriptionscid
updateSkipContentSkip next order (pause 1 month)scid
updateShipNowContentTrigger immediate shipmentscid, info[update_billing_date]
updateChargeNowContentTrigger immediate chargescid, info[update_billing_date]
updateItemContentUpdate item quantityscid, info[line], info[qty]
removeItemContentRemove item from subscriptionscid, info[line]
swapProductSwap product in subscriptionscid, info[line], info[product_id]
ShowSwapContentGet available products for swapscid, info[productAction]
updateRefundContentRequest refundscid, order_gid (optional)

Response Format

Response Codes

CodeMeaningDescription
result: 1SuccessAction completed successfully
result: -1Validation ErrorInvalid customer, contract not found, or invalid payment method
result: -2UnauthorizedCustomer not allowed to perform this action
result: 9Rate LimitedToo 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

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.');
    }
});

Liquid Form Examples

Pause Form

<form action="{{ shop.url }}/apps/subscribfy-api/store/manage-membership-dashboard" method="POST">
  <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 action="{{ shop.url }}/apps/subscribfy-api/store/manage-membership-dashboard" method="POST">
  <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

ActionParent (Gift Giver)Recipient
CancelYesYes
PauseYesNo
ReactivateYesNo
Update PaymentNoNo

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 %}
  <h3>Memberships you've gifted:</h3>
  <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

On this page

AI Chat

Ask a question about Subscribfy