Comprehensive API documentation for managing subscriptions, payments, and related operations. These APIs allow you to programmatically manage subscription lifecycles, handle payments, configure products, and integrate subscription functionality into your applications.
- Get current billing cycle number for a subscription contract
External APIs (0.0.1)
Request
Retrieves fulfillment information for the most recent successful order associated with a subscription contract. This includes tracking details, shipment status, delivery estimates, and fulfillment line items from Shopify.
What This Endpoint Returns: Complete fulfillment data for the subscription's last successfully billed order, queried in real-time from Shopify's GraphQL API. Shows customers when and how their subscription items will be (or were) delivered.
Fulfillment Data Included:
Shipment Tracking:
- Tracking numbers and URLs
- Carrier information (USPS, FedEx, UPS, etc.)
- Tracking company details
- Tracking status updates
Delivery Status:
- Fulfillment status (fulfilled, in_transit, out_for_delivery, delivered, etc.)
- Estimated delivery date
- Actual delivery timestamp (if delivered)
- Delivery method (shipping, pickup, local delivery)
Fulfillment Details:
- Fulfilled line items (which products shipped)
- Quantities fulfilled
- Fulfillment service (manual, automated, 3PL)
- Fulfillment created/updated dates
- Multiple fulfillments (if order shipped in parts)
Location Information:
- Fulfillment location/warehouse
- Origin address details
- Fulfillment service name
Order Determination Logic:
Which Order is Retrieved:
- Finds most recent SUCCESS billing attempt for contract
- If found: Uses that order's Shopify order ID
- If none: Falls back to subscription's origin order ID
- Queries Shopify for that order's fulfillments
Why This Matters:
- Shows CURRENT/LATEST fulfillment status
- Not historical fulfillments from months ago
- Reflects what customer is waiting for NOW
- Updates after each new billing cycle
Use Cases:
1. Customer Portal "Where's My Order":
- Display tracking information
- Show estimated delivery date
- Provide carrier tracking links
- Update fulfillment status
2. Subscription Order Tracking:
- Track recurring order deliveries
- Monitor fulfillment progress
- Identify delayed shipments
- Provide proactive delivery updates
3. Customer Support:
- Answer "where is my order" questions
- Verify shipment details
- Troubleshoot delivery issues
- Provide accurate tracking info
4. Automated Notifications:
- Send tracking emails automatically
- Notify customers of shipments
- Alert on delivery completion
- Trigger review request flows
5. Subscription Management:
- Show fulfillment in subscription history
- Display delivery patterns
- Track fulfillment reliability
- Monitor shipping performance
Response Structure:
Returns Shopify Order object with fulfillments:
{
"id": "gid://shopify/Order/123456789",
"name": "#1001",
"fulfillmentOrders": {
"edges": [
{
"node": {
"id": "gid://shopify/FulfillmentOrder/111111",
"status": "SUCCESS",
"fulfillments": {
"edges": [
{
"node": {
"trackingInfo": [
{
"number": "1Z999AA10123456784",
"url": "https://wwwapps.ups.com/tracking/...",
"company": "UPS"
}
],
"status": "IN_TRANSIT",
"estimatedDeliveryAt": "2024-03-20T00:00:00Z",
"deliveredAt": null
}
}
]
}
}
}
]
}
}Common Scenarios:
Scenario: Order Fulfilled & Shipped
{
"trackingInfo": [{"number": "9400...", "company": "USPS"}],
"status": "IN_TRANSIT",
"estimatedDeliveryAt": "2024-03-18T00:00:00Z"
}Scenario: Order Delivered
{
"status": "DELIVERED",
"deliveredAt": "2024-03-15T14:23:00Z"
}Scenario: Not Yet Fulfilled
{
"fulfillmentOrders": {
"edges": [
{
"node": {
"status": "OPEN",
"fulfillments": {"edges": []}
}
}
]
}
}Scenario: No Order Yet (New Subscription) Returns null or empty response
Important Considerations:
Real-Time Shopify Query:
- Makes live call to Shopify GraphQL API
- NOT cached data
- Response time: 500-1500ms
- Subject to Shopify rate limits
Data Freshness:
- Returns current fulfillment status from Shopify
- Tracking updates reflect Shopify's data
- May lag behind carrier's actual status
- Shopify updates tracking periodically
Null Responses:
- Returns null if no orders exist yet
- Returns null if order ID not found
- Handle gracefully in UI
Multiple Fulfillments:
- Order may have multiple fulfillments (split shipments)
- Each fulfillment has own tracking
- Iterate through all fulfillment nodes
Integration Example:
Customer Portal - Track Shipment:
const order = await fetch(
`/api/external/v2/subscription-contract-details/subscription-fulfillments/${contractId}`,
{ headers: { 'X-API-Key': 'your-key' } }
).then(r => r.json());
if (!order) {
return '<p>No shipment information available yet.</p>';
}
const fulfillments = order.fulfillmentOrders?.edges || [];
fulfillments.forEach(fo => {
fo.node.fulfillments?.edges?.forEach(f => {
const tracking = f.node.trackingInfo?.[0];
if (tracking) {
console.log(`Track with ${tracking.company}: ${tracking.number}`);
console.log(`URL: ${tracking.url}`);
}
});
});Best Practices:
- Cache Response: Cache for 30-60 minutes to reduce Shopify API calls
- Handle Nulls: Always check for null/empty responses
- Parse GraphQL: Navigate edges/nodes structure carefully
- Show All Tracking: Display all fulfillments if multiple shipments
- Link to Carrier: Provide clickable tracking URLs
Authentication: Requires valid X-API-Key header
- https://subscription-admin.appstle.com/api/external/v2/subscription-contract-details/subscription-fulfillments/{contractId}
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
curl -i -X GET \
'https://subscription-admin.appstle.com/api/external/v2/subscription-contract-details/subscription-fulfillments/{contractId}?api_key=string' \
-H 'X-API-Key: string'{ "get__typename": "string", "id": "string", "fulfillmentOrders": { "get__typename": "string", "nodes": [ … ], "pageInfo": { … } } }
Request
Retrieves the current billing cycle number for a specific subscription contract. The cycle number represents how many successful billing attempts have occurred for this subscription, starting from 1 for the initial order.
What is a Billing Cycle? A billing cycle represents one completed billing period in a subscription's lifetime. Each successful billing attempt increments the cycle count. This number is crucial for:
- Tracking subscription progress towards minimum/maximum cycle limits
- Applying cycle-based pricing adjustments (discounts after N cycles)
- Determining eligibility for cancellation (minimum cycles requirement)
- Calculating customer lifetime value
- Analyzing subscription retention metrics
How Cycle Counting Works:
Initial Order:
- Cycle 1 starts when subscription is first created
- Initial order counts as the first billing cycle
- Includes origin order that created the subscription
Subsequent Orders:
- Each successful billing attempt increments cycle by 1
- Only SUCCESS status billing attempts are counted
- Failed/skipped billing attempts do NOT increment cycle
- Paused subscriptions maintain their current cycle number
Calculation Formula:
Current Cycle = 1 + (Number of Successful Billing Attempts)Example Timeline:
- Day 1: Subscription created, initial order → Cycle 1
- Day 30: First recurring order successful → Cycle 2
- Day 60: Second recurring order successful → Cycle 3
- Day 90: Billing fails (payment declined) → Still Cycle 3
- Day 95: Retry successful → Cycle 4
Use Cases:
1. Cancellation Eligibility:
- Verify customer has met minimum cycle requirement
- Enforce contract terms (e.g., "3 month minimum")
- Display "Can cancel after N more orders" messaging
- Block premature cancellations
2. Pricing Adjustments:
- Apply introductory pricing for first N cycles
- Trigger loyalty discounts after X cycles
- Calculate when pricing changes take effect
- Implement "First 3 months 50% off" promotions
3. Customer Retention:
- Identify subscriptions at risky cycle counts
- Send retention campaigns at specific milestones
- Track average cycles before churn
- Celebrate subscription anniversaries
4. Analytics & Reporting:
- Calculate customer lifetime value (cycle × price)
- Analyze subscription duration distribution
- Track retention curves by cohort
- Measure success of lifecycle campaigns
5. Customer Portal Display:
- Show "Order #X of Y" progress indicators
- Display remaining cycles until cancellation allowed
- Show subscription tenure/loyalty status
- Calculate and display subscription value earned
Response Format:
Returns a single integer representing the current cycle number:
3Response Examples:
New subscription (just created):
1After 5 successful billing attempts:
6Note: Initial order (1) + 5 successful renewals = Cycle 6
Important Considerations:
Cycle vs. Billing Attempts:
- Failed billing attempts don't increment cycle
- Skipped orders don't increment cycle
- Manual order creation may not increment cycle
- Cycle represents successful billing events only
Paused Subscriptions:
- Cycle number remains frozen while paused
- Resumes at same cycle when un-paused
- Pause duration doesn't affect cycle count
Minimum/Maximum Cycles:
minCycles: Minimum cycles before cancellation allowedmaxCycles: Subscription auto-expires after this many cycles- Use this endpoint to check progress towards these limits
Data Source:
- Queries Appstle database (not Shopify API)
- Counts records in subscription_billing_attempt table
- Filters by SUCCESS status only
- Fast response time (< 100ms typically)
Integration Example:
Check if customer can cancel:
// Get subscription details
const contract = await getSubscriptionContract(contractId);
const currentCycle = await fetch(
`/api/external/v2/subscription-contract-details/current-cycle/${contractId}`,
{ headers: { 'X-API-Key': 'your-key' } }
).then(r => r.json());
const minCycles = contract.minCycles || 0;
if (currentCycle >= minCycles) {
console.log('Customer can cancel now');
showCancelButton();
} else {
const cyclesRemaining = minCycles - currentCycle;
console.log(`Must complete ${cyclesRemaining} more orders before canceling`);
showMinimumCommitmentMessage(cyclesRemaining);
}Display progress to max cycles:
const currentCycle = await getCycleNumber(contractId);
const maxCycles = contract.maxCycles;
if (maxCycles) {
const progress = (currentCycle / maxCycles) * 100;
console.log(`Subscription ${progress.toFixed(0)}% complete (${currentCycle}/${maxCycles})`);
if (currentCycle === maxCycles) {
console.log('This is the final cycle - subscription will expire after this order');
}
}Performance Characteristics:
Fast Query:
- Simple database count query
- Indexed by shop and contractId
- Typical response time: 50-150ms
- Suitable for real-time UI updates
Best Practices:
- Cache Results: Cache cycle number briefly (few minutes) to reduce API calls
- Combine with Contract Data: Fetch contract details simultaneously for min/max cycles
- Handle Edge Cases: Account for subscriptions with no successful billings yet
- Display Progress: Show cycle number in customer-friendly format ("Order 3 of 12")
- Sync with Billing: Update cycle number after each billing attempt completes
Common Misunderstandings:
Myth: Cycle = Months Subscribed
- Reality: Cycle = Successful billing attempts, not time elapsed
- A paused subscription stays at same cycle for months
- Failed payments don't advance the cycle
Myth: First order is Cycle 0
- Reality: Cycles start at 1, not 0
- Initial/origin order is Cycle 1
Related Fields:
minCycles: Minimum cycles before cancellation (from contract)maxCycles: Maximum cycles before auto-expiry (from contract)billingInterval: Frequency between cycles (from contract)
Authentication: Requires valid X-API-Key header
- https://subscription-admin.appstle.com/api/external/v2/subscription-contract-details/current-cycle/{contractId}
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
curl -i -X GET \
'https://subscription-admin.appstle.com/api/external/v2/subscription-contract-details/current-cycle/{contractId}' \
-H 'X-API-Key: sk_live_1234567890abcdef'1
Request
Retrieves all available billing frequency options configured for specific Shopify selling plan(s). Returns the complete set of billing intervals that customers can choose from, including frequency, interval type, and any associated discounts or pricing policies.
What This Endpoint Does: Queries the subscription group plans database to find all frequency configurations associated with given selling plan ID(s). This is essential for building subscription frequency selectors in customer portals or during subscription modifications.
Key Concepts:
Selling Plans:
- Shopify's mechanism for defining subscription options
- Each product variant can have multiple selling plans
- Selling plans define billing/delivery frequency
- Plans are grouped in subscription groups
Frequency Info:
- Specific billing interval configuration
- E.g., "Every 2 weeks", "Every month", "Every 3 months"
- Includes pricing policies and discounts
- Customers select from available frequencies
Request Parameters:
sellingPlanIds (required):
- Comma-separated list of Shopify selling plan IDs
- Example:
"123456,123457,123458" - Can query single or multiple plans
- Returns frequencies for ALL provided plans
Lookup Process:
- Parse selling plan IDs from comma-separated string
- Find subscription groups containing these plans
- Extract frequency configurations from group JSON
- Return all matching frequency options
- Deduplicate if same frequency appears multiple times
Response Data Included:
For Each Frequency Option:
- id: Selling plan ID
- frequencyName: Display name (e.g., "Monthly")
- interval: WEEK, MONTH, or YEAR
- intervalCount: Number of intervals (e.g., 2 for bi-weekly)
- deliveryInterval: Same or different from billing
- deliveryIntervalCount: Delivery frequency
- pricingPolicy: Discount configuration
- billingPolicy: Min/max cycles, anchor settings
Pricing Policy Details:
- Discount type (percentage, fixed amount)
- Discount value
- After cycle discounts (e.g., "50% off first 3 months")
- Adjustment type
Use Cases:
1. Frequency Selection UI:
- Build dropdown/radio list of frequency options
- Show available intervals to customers
- Display pricing for each frequency
- Enable subscription frequency changes
2. Subscription Modification:
- Show current frequency and alternatives
- Allow customers to switch frequencies
- Validate new frequency selection
- Preview pricing changes
3. Product Page:
- Display subscription frequency options
- Show "Subscribe and save" pricing
- Calculate savings per frequency
- Build subscription purchase selectors
4. Customer Portal:
- "Change Frequency" functionality
- Show all available options
- Highlight current selection
- Display pricing differences
Response Format:
[
{
"id": "123456",
"frequencyName": "Every 2 Weeks",
"interval": "WEEK",
"intervalCount": 2,
"deliveryInterval": "WEEK",
"deliveryIntervalCount": 2,
"pricingPolicy": {
"adjustmentType": "PERCENTAGE",
"adjustmentValue": "10.0"
}
},
{
"id": "123457",
"frequencyName": "Monthly",
"interval": "MONTH",
"intervalCount": 1,
"deliveryInterval": "MONTH",
"deliveryIntervalCount": 1,
"pricingPolicy": {
"adjustmentType": "PERCENTAGE",
"adjustmentValue": "15.0"
}
}
]Integration Example:
Customer Portal - Frequency Selector:
// Get selling plan from current subscription
const currentSellingPlanId = subscription.sellingPlanId;
// Fetch available frequencies
const frequencies = await fetch(
`/api/external/v2/subscription-contract-details/billing-interval?sellingPlanIds=${currentSellingPlanId}`,
{ headers: { 'X-API-Key': 'your-key' } }
).then(r => r.json());
// Build selector
const selector = frequencies.map(freq => `
<option value="${freq.id}"
${freq.id === currentSellingPlanId ? 'selected' : ''}>
${freq.frequencyName} - Save ${freq.pricingPolicy.adjustmentValue}%
</option>
`).join('');
document.querySelector('#frequency-select').innerHTML = selector;Important Considerations:
Data Source:
- Queries Appstle database (NOT Shopify API)
- Based on subscription group configuration
- Fast response (< 100ms typically)
- Data synced when groups are updated
Multiple Selling Plans:
- Can query multiple plans at once
- Returns union of all frequencies
- Useful for products with multiple subscription options
- Results may contain duplicates if plans share frequencies
Empty Results:
- Returns empty array
[]if no plans found - Returns empty if selling plan ID invalid
- Not an error - handle gracefully
Best Practices:
- Query Relevant Plans: Only query selling plans for current product/variant
- Display Discounts: Show savings clearly in UI
- Sort by Interval: Order options logically (weekly → monthly → yearly)
- Highlight Current: Clearly mark customer's current frequency
- Cache Results: Cache frequency data per selling plan
Authentication: Requires valid X-API-Key header
- https://subscription-admin.appstle.com/api/external/v2/subscription-contract-details/billing-interval
- curl
- JavaScript
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
- Payload
curl -i -X GET \
'https://subscription-admin.appstle.com/api/external/v2/subscription-contract-details/billing-interval?api_key=string&sellingPlanIds=string' \
-H 'X-API-Key: string'[ { "frequencyCount": 0, "frequencyInterval": "DAY", "billingFrequencyCount": 0, "payAsYouGoPrepaidBillingFrequencyCount": 0, "billingFrequencyInterval": "DAY", "frequencyName": "string", "frequencyDescription": "string", "discountOffer": 0.1, "discountOffer2": 0.1, "afterCycle1": 0, "afterCycle2": 0, "discountType": "PERCENTAGE", "discountType2": "PERCENTAGE", "discountEnabled": true, "discountEnabled2": true, "discountEnabledMasked": true, "discountEnabled2Masked": true, "id": "string", "frequencyType": "ON_PURCHASE_DAY", "specificDayValue": 0, "specificMonthValue": 0, "specificDayEnabled": true, "maxCycles": 0, "minCycles": 0, "cutOff": 0, "prepaidFlag": "string", "idNew": "string", "planType": "PAY_AS_YOU_GO", "deliveryPolicyPreAnchorBehavior": "ASAP", "freeTrialEnabled": true, "freeTrialCount": 0, "freeTrialInterval": "DAY", "memberOnly": true, "nonMemberOnly": true, "memberInclusiveTags": "string", "memberExclusiveTags": "string", "formFieldJson": "string", "upcomingOrderEmailBuffer": 0, "frequencySequence": 0, "groupName": "string", "groupId": 0, "repeatingCycle": true, "repeatingNumberOfCycle": 0, "keepOriginalNextBillingDateAfterTrial": true, "defaultSelectedPlan": true, "inventoryPolicyReserve": "ON_FULFILLMENT", "appstleCycles": [ … ] } ]