New features available! Check the changelog
Subscribfy
Store Credits

Store Credit Management API

Backend API for retrieving and updating customer store credit balances. Every update is recorded in the customer's store credit history.

Read and adjust customer store credits from your own backend. Use this API to check a balance, add credits, or deduct credits with a recorded reason.

Replacing the legacy endpoint

This page documents the current endpoint. The legacy Store Credit Management API is deprecated and will be retired at the end of 2026. See Migrating from the legacy endpoint.

Endpoint

PropertyValue
URLhttps://app1.subscribfy.com/api/v1/store-credit/manage
MethodPOST
Content-Typeapplication/x-www-form-urlencoded or application/json
Acceptapplication/json (recommended)

Responses are JSON. Send Accept: application/json so unexpected server errors are also returned as JSON.

Authentication

Every request must include your Subscribfy API key as the key field in the POST body. To find your key, see Subscribfy API Key.

Keep your API key private

The key gives read and write access to your customers' store credit balances.

  • Send it only in the POST body, never in the URL. URLs end up in server logs, proxies and browser history.
  • Call this API from your own server only. Never put the key in theme code, checkout extensions, mobile apps or browser JavaScript.
  • If the key is ever exposed, contact support to regenerate it.

Parameters

Required for All Requests

Prop

Type

Additional Parameters for action=update

Prop

Type

Update Types

Send the value exactly as shown, including spaces and lowercase letters.

TypeDescriptionAllowed update_value
manual admin adjustmentManual adjustment by adminPositive or negative
reconciledCredit reconciliation/correctionNegative only
forfeitForfeited credits (e.g., policy violation)Negative only
expiredExpired credits removalNegative only

Use forfeit, not forfeited. Any value not listed above is rejected with a 422 validation error. An update_value of 0 or with more than 2 decimal places is also rejected with 422.

Actions

Get Balance (action=get)

Retrieve a customer's current store credit balance.

curl -X POST "https://app1.subscribfy.com/api/v1/store-credit/manage" \
  -H "Accept: application/json" \
  --data-urlencode "key=$SUBSCRIBFY_API_KEY" \
  --data-urlencode "cid=7211677024514" \
  --data-urlencode "email=customer@example.com" \
  --data-urlencode "action=get"

Response (200):

{
  "cid": 7211677024514,
  "email": "customer@example.com",
  "currency": "USD",
  "store_credit_balance": 52.5,
  "store_credit_in_use_at_checkout": 0
}

Response Fields:

Prop

Type

Amounts are JSON numbers. Whole amounts have no decimal part (50, not 50.00).

About store_credit_in_use_at_checkout

When a customer applies store credits at checkout, that amount is temporarily reserved to prevent double-spending:

  • Reserved credits appear in store_credit_in_use_at_checkout
  • store_credit_balance shows remaining available credit
  • If order completes: reserved credits are consumed
  • If checkout abandoned: reserved credits are released back to balance, no earlier than 30 minutes after they were reserved

Update Balance (action=update)

Add or deduct store credits from a customer's account.

curl -X POST "https://app1.subscribfy.com/api/v1/store-credit/manage" \
  -H "Accept: application/json" \
  --data-urlencode "key=$SUBSCRIBFY_API_KEY" \
  --data-urlencode "cid=7211677024514" \
  --data-urlencode "email=customer@example.com" \
  --data-urlencode "action=update" \
  --data-urlencode "update_value=-10" \
  --data-urlencode "update_reason=Store credit converted to gift card" \
  --data-urlencode "update_type=reconciled"

Response (200):

{
  "cid": 7211677024514,
  "email": "customer@example.com",
  "currency": "USD",
  "store_credit_balance": 42.5,
  "result": {
    "status": "success"
  }
}

store_credit_balance is the balance after the update. cid, email and currency have the same meaning as in the get response.

How deductions are applied

  • If a deduction is larger than the balance, the balance is set to 0 and only the available amount is deducted. The request still succeeds.
  • If the balance is already 0, a deduction fails with a 400 error.
  • Updates are not idempotent. Each successful call is applied again, so do not blindly retry after a timeout, a 400 or a 500; call action=get first to see whether the change was applied.

Code Examples

function subscribfyStoreCredit(array $params): array
{
    $ch = curl_init('https://app1.subscribfy.com/api/v1/store-credit/manage');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Accept: application/json'],
        CURLOPT_POSTFIELDS => http_build_query(['key' => getenv('SUBSCRIBFY_API_KEY')] + $params),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 15,
    ]);

    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($body === false) {
        throw new RuntimeException('Request to Subscribfy failed');
    }

    $data = json_decode($body, true);
    if ($status !== 200) {
        throw new RuntimeException($data['message'] ?? $data['error'] ?? "HTTP $status");
    }

    return $data;
}

// Get balance
$balance = subscribfyStoreCredit([
    'cid' => '7211677024514',
    'email' => 'customer@example.com',
    'action' => 'get',
]);
echo 'Balance: ' . $balance['store_credit_balance'];

// Add credits
subscribfyStoreCredit([
    'cid' => '7211677024514',
    'email' => 'customer@example.com',
    'action' => 'update',
    'update_value' => 25,
    'update_reason' => 'Loyalty reward',
    'update_type' => 'manual admin adjustment',
]);

// Deduct credits
subscribfyStoreCredit([
    'cid' => '7211677024514',
    'email' => 'customer@example.com',
    'action' => 'update',
    'update_value' => -10,
    'update_reason' => 'Converted to gift card',
    'update_type' => 'reconciled',
]);
const ENDPOINT = 'https://app1.subscribfy.com/api/v1/store-credit/manage';

async function subscribfyStoreCredit(params) {
  const response = await fetch(ENDPOINT, {
    method: 'POST',
    headers: { Accept: 'application/json' },
    body: new URLSearchParams({ key: process.env.SUBSCRIBFY_API_KEY, ...params }),
    signal: AbortSignal.timeout(15000),
  });

  const data = await response.json();
  if (!response.ok) {
    throw new Error(data.message ?? data.error ?? `HTTP ${response.status}`);
  }

  return data;
}

// Get balance
const balance = await subscribfyStoreCredit({
  cid: '7211677024514',
  email: 'customer@example.com',
  action: 'get',
});
console.log(`Balance: ${balance.store_credit_balance}`);

// Add credits
await subscribfyStoreCredit({
  cid: '7211677024514',
  email: 'customer@example.com',
  action: 'update',
  update_value: '25',
  update_reason: 'Loyalty reward',
  update_type: 'manual admin adjustment',
});

// Deduct credits
await subscribfyStoreCredit({
  cid: '7211677024514',
  email: 'customer@example.com',
  action: 'update',
  update_value: '-10',
  update_reason: 'Converted to gift card',
  update_type: 'reconciled',
});
import os
import requests

ENDPOINT = 'https://app1.subscribfy.com/api/v1/store-credit/manage'

def subscribfy_store_credit(**params):
    response = requests.post(
        ENDPOINT,
        data={'key': os.environ['SUBSCRIBFY_API_KEY'], **params},
        headers={'Accept': 'application/json'},
        timeout=15,
    )
    data = response.json()
    if not response.ok:
        raise RuntimeError(data.get('message') or data.get('error') or f'HTTP {response.status_code}')
    return data

# Get balance
balance = subscribfy_store_credit(cid='7211677024514', email='customer@example.com', action='get')
print(f"Balance: {balance['store_credit_balance']}")

# Add credits
subscribfy_store_credit(
    cid='7211677024514',
    email='customer@example.com',
    action='update',
    update_value=25,
    update_reason='Loyalty reward',
    update_type='manual admin adjustment',
)

# Deduct credits
subscribfy_store_credit(
    cid='7211677024514',
    email='customer@example.com',
    action='update',
    update_value=-10,
    update_reason='Converted to gift card',
    update_type='reconciled',
)

Error Responses

Errors return a non-200 HTTP status with a JSON body. Check the status code to detect errors. The error text is in message when present, otherwise in error.

StatusResponse bodyCause
401{"message": "Subscribfy API key is missing."}No key in the request body
401{"message": "Subscribfy API key is incorrect."}Key not found, or the store is not active
422Validation error (see below)A parameter is missing or invalid
404{"error": "Customer not found."}No customer in your store matches both cid and email
400{"error": "Failed to update store credit: The current balance cannot be below 0."}Deduction requested while the balance is already 0
400{"error": "Failed to update store credit: ..."}The update could not be completed
500{"message": "Server Error"}Unexpected server error. Send Accept: application/json to make sure this body is JSON.

Validation Errors (422)

{
  "error": true,
  "message": "Sorry, the information provided is not valid.",
  "data": "{\"update_type\":[\"The selected update type is invalid.\"]}"
}

data is a JSON-encoded string, not an object. Decode it to read the messages for each field.

FieldExample message
cidThe cid field is required.
emailThe email field must be a valid email address.
actionThe selected action is invalid.
update_valueThe update value field must be a number.
update_valueThe update value field must have 0-2 decimal places.
update_valueupdate_value must not be 0.
update_valueFor 'reconciled', update_value must not be positive.
update_reasonThe update reason field is required when action is update.
update_typeThe selected update type is invalid.

Migrating from the Legacy Endpoint

The legacy endpoint https://subscribfy.com/shopify-app/api/v1/store-credit-management-api.php is deprecated and will be retired at the end of 2026. Parameter names and actions are the same. To switch:

  1. Change the URL to https://app1.subscribfy.com/api/v1/store-credit/manage.
  2. Keep sending key and all other parameters in the POST body.
  3. Send forfeit as the update type, not forfeited.
  4. Send a non-zero update_value with at most 2 decimal places.
  5. Read the customer ID from cid in responses. The legacy page documents this field as gid. It is a JSON number.
  6. Expect a new currency field in responses.
  7. Check the HTTP status code to detect errors (see Error Responses).

Best Practices

  • Call from your server only - Keep the API key out of anything a customer can see
  • Validate email match - Email must match the customer record, or the request returns 404
  • Use descriptive reasons - update_reason is saved to the store credit history
  • Check balance before deducting - Deductions larger than the balance are reduced to the available amount
  • Log all transactions - Keep records of API calls and responses for reconciliation
  • Handle checkout reserves - store_credit_balance already excludes reserved credits; do not subtract store_credit_in_use_at_checkout from it again
  • Do not retry updates blindly - Check the balance with action=get before retrying an update that timed out or failed

Was this page helpful?

On this page

AI Chat

Ask a question about Subscribfy