Retrieve Build-A-Box bundle details by handle for customer storefront
Fetches comprehensive Build-A-Box bundle configuration using a user-friendly handle (URL slug) instead of numeric ID. This endpoint is specifically designed for customer-facing storefronts and integration scenarios where you want to reference bundles by their human-readable handles rather than database IDs.
Key Differences from GET /:
- Lookup Method: Uses bundle handle (e.g., ‘premium-coffee-box’) instead of numeric ID (e.g., 12345)
- Response Format: Returns SubscriptionBundlingResponseV3 with enhanced structure
- Customer Focus: Optimized for storefront display and customer selection workflows
- Bundle + Subscription: Returns both bundle details AND associated subscription plan information
- Public Access: Designed for integration in customer-facing applications
What is a Bundle Handle? A handle is a URL-friendly identifier for a bundle:
- Format: lowercase letters, numbers, and hyphens only
- Example:
premium-coffee-selection,monthly-snack-box,beauty-essentials - Stable: Unlike IDs, handles remain consistent across environments
- SEO-Friendly: Can be used in customer-facing URLs
- Human-Readable: Easy to remember and communicate
Response Structure (SubscriptionBundlingResponseV3):
{
"bundle": { // Complete bundle configuration
"id": 45678,
"bundleName": "Premium Coffee Selection",
"bundleHandle": "premium-coffee-selection",
"description": "...",
"products": [...],
"pricing": {...},
"active": true
},
"subscription": { // Associated subscription plan details
"planId": 98765,
"frequencies": [...],
"deliveryOptions": {...},
"sellingPlan": {...}
}
}
Primary Use Cases:
- Storefront Integration: Display bundle details on product pages
- Customer Portal: Allow customers to browse available bundles
- Landing Pages: Create dedicated pages for specific bundles using handles
- Marketing Campaigns: Link directly to bundles with memorable URLs
- Cross-Platform Sync: Use stable handles for multi-platform integrations
- Mobile Apps: Retrieve bundle data using consistent identifiers
- External Integrations: Third-party systems can reference bundles by handle
When to Use Handle vs ID:
-
Use Handle when:
- Building customer-facing features
- Creating shareable URLs
- Syncing across environments (dev → staging → prod)
- External systems need stable references
- SEO and user experience are priorities
-
Use ID when:
- You already have the numeric ID from another API response
- Performing administrative operations
- Internal system integration
- Optimizing for query performance
Availability Validation: The endpoint validates that:
- Bundle exists and belongs to the specified shop
- Bundle has an associated subscription configuration
- If validation fails, returns 404 Not Found
- Both bundle AND subscription must be present for success response
Integration Examples: Storefront Display:
// Fetch bundle for display on product page
const handle = 'premium-coffee-selection';
const response = await fetch(
`/api/external/v2/build-a-box/${handle}`,
{ headers: { 'X-API-Key': 'your-api-key' } }
);
const { bundle, subscription } = await response.json();
displayBundleOptions(bundle, subscription);
Best Practices:
- Handle Naming: Use descriptive, SEO-friendly handles
- Caching: Cache bundle data with handle as key for better performance
- Error Handling: Always check for 404 - bundle may be deleted or deactivated
- Environment Sync: Use handles to maintain consistency across environments
- URL Structure: Incorporate handles into clean, readable URLs
- Documentation: Document handle conventions for your team
Performance Considerations:
- Handle lookups may be slightly slower than ID lookups
- Response includes full bundle + subscription data (larger payload)
- Consider caching for frequently accessed bundles
- Returns complete product catalog for the bundle
Authentication: Requires valid X-API-Key header or api_key parameter (deprecated)
curl --request GET \
--url https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}import requests
url = "https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));const url = 'https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}';
const options = {method: 'GET'};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"bundle": {
"active": true,
"allowOneTimePurchase": true,
"buildABoxType": "SINGLE_PRODUCT",
"buildBoxVersion": "V2",
"bundleHandle": "premium-coffee-selection",
"bundleName": "Premium Coffee Selection",
"description": "Choose your favorite coffee blends for monthly delivery",
"discount": 10,
"discountType": "PERCENTAGE",
"id": 45678,
"maxProductCount": 5,
"minProductCount": 2,
"products": [
{
"imageUrl": "https://cdn.shopify.com/coffee-medium.jpg",
"price": "14.99",
"productId": 111111,
"title": "Medium Roast Coffee - 12oz",
"variantId": 222222
}
],
"shop": "example-shop.myshopify.com",
"uniqueRef": "bab_abc123xyz"
},
"createdAt": "2024-03-15T10:30:00Z",
"subscription": {
"deliveryPolicy": {
"anchors": [],
"type": "RECURRING"
},
"frequencies": [
{
"displayName": "Deliver every month",
"interval": "MONTH",
"intervalCount": 1
},
{
"displayName": "Deliver every 2 weeks",
"interval": "WEEK",
"intervalCount": 2
}
],
"sellingPlanGroupId": "gid://shopify/SellingPlanGroup/123456",
"subscriptionPlanId": 98765
},
"updatedAt": "2024-03-20T14:45:00Z"
}Headers
Path Parameters
Bundle Handle
Query Parameters
API Key (Deprecated - Use Header X-API-Key instead)
Response
Bundle and subscription details successfully retrieved
curl --request GET \
--url https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}import requests
url = "https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));const url = 'https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}';
const options = {method: 'GET'};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://subscription-admin.appstle.com/api/external/v2/build-a-box/{handle}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"bundle": {
"active": true,
"allowOneTimePurchase": true,
"buildABoxType": "SINGLE_PRODUCT",
"buildBoxVersion": "V2",
"bundleHandle": "premium-coffee-selection",
"bundleName": "Premium Coffee Selection",
"description": "Choose your favorite coffee blends for monthly delivery",
"discount": 10,
"discountType": "PERCENTAGE",
"id": 45678,
"maxProductCount": 5,
"minProductCount": 2,
"products": [
{
"imageUrl": "https://cdn.shopify.com/coffee-medium.jpg",
"price": "14.99",
"productId": 111111,
"title": "Medium Roast Coffee - 12oz",
"variantId": 222222
}
],
"shop": "example-shop.myshopify.com",
"uniqueRef": "bab_abc123xyz"
},
"createdAt": "2024-03-15T10:30:00Z",
"subscription": {
"deliveryPolicy": {
"anchors": [],
"type": "RECURRING"
},
"frequencies": [
{
"displayName": "Deliver every month",
"interval": "MONTH",
"intervalCount": 1
},
{
"displayName": "Deliver every 2 weeks",
"interval": "WEEK",
"intervalCount": 2
}
],
"sellingPlanGroupId": "gid://shopify/SellingPlanGroup/123456",
"subscriptionPlanId": 98765
},
"updatedAt": "2024-03-20T14:45:00Z"
}