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
| Property | Value |
|---|---|
| URL | https://app1.subscribfy.com/api/v1/store-credit/manage |
| Method | POST |
| Content-Type | application/x-www-form-urlencoded or application/json |
| Accept | application/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.
| Type | Description | Allowed update_value |
|---|---|---|
manual admin adjustment | Manual adjustment by admin | Positive or negative |
reconciled | Credit reconciliation/correction | Negative only |
forfeit | Forfeited credits (e.g., policy violation) | Negative only |
expired | Expired credits removal | Negative 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_balanceshows 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
400error. - Updates are not idempotent. Each successful call is applied again, so do not blindly retry after a timeout, a
400or a500; callaction=getfirst 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.
| Status | Response body | Cause |
|---|---|---|
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 |
422 | Validation 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.
| Field | Example message |
|---|---|
cid | The cid field is required. |
email | The email field must be a valid email address. |
action | The selected action is invalid. |
update_value | The update value field must be a number. |
update_value | The update value field must have 0-2 decimal places. |
update_value | update_value must not be 0. |
update_value | For 'reconciled', update_value must not be positive. |
update_reason | The update reason field is required when action is update. |
update_type | The 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:
- Change the URL to
https://app1.subscribfy.com/api/v1/store-credit/manage. - Keep sending
keyand all other parameters in the POST body. - Send
forfeitas the update type, notforfeited. - Send a non-zero
update_valuewith at most 2 decimal places. - Read the customer ID from
cidin responses. The legacy page documents this field asgid. It is a JSON number. - Expect a new
currencyfield in responses. - 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_reasonis 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_balancealready excludes reserved credits; do not subtractstore_credit_in_use_at_checkoutfrom it again - Do not retry updates blindly - Check the balance with
action=getbefore retrying an update that timed out or failed
Was this page helpful?