Api Reference
This guide shows you how to pull Available to Promise (ATP) and availability data, then build a low-stock monitoring system.
inventory.read permissionRetrieve ATP values for all products across channels at a specific location.
Request:
1curl -X GET "https://app.betterdata.co/api/inventory/channel-location?locationId=loc_123&page=1&limit=100" \2 -H "Authorization: Bearer YOUR_API_KEY" \3 -H "Content-Type: application/json"Response:
1{2 "products": [3 {4 "productMasterId": "prod_123",5 "productName": "Product Name",6 "globalSku": "SKU-123",7 "channels": [8 {9 "channelId": "DTC",10 "channelName": "Direct to Consumer",11 "atp": 150,12 "onHand": 200,13 "reserved": 50,14 "available": 150,15 "severity": "OK",16 "threshold": 10,17 "policyId": "policy_123"18 },19 {20 "channelId": "WHOLESALE",21 "channelName": "Wholesale",22 "atp": 5,23 "onHand": 20,24 "reserved": 15,25 "available": 5,26 "severity": "LOW",27 "threshold": 10,28 "policyId": "policy_456"29 }30 ],31 "totalOnHand": 220,32 "totalReserved": 65,33 "minAtp": 5,34 "maxAtp": 150,35 "hasCritical": false36 }37 ],38 "channels": [39 {40 "channelId": "DTC",41 "channelName": "Direct to Consumer",42 "channelType": "E_COMMERCE",43 "policyId": "policy_123",44 "lowAtpThreshold": 1045 }46 ],47 "location": {48 "id": "loc_123",49 "name": "Main Warehouse"50 },51 "pagination": {52 "page": 1,53 "pageSize": 100,54 "total": 500,55 "totalPages": 556 }57}Notes:
OK, LOW, CRITICALATP = On-Hand - Reserved - Safety Stockpage and limit to retrieve all productsFilter products with low or critical ATP.
Example Logic:
1function identifyLowStock(products) {2 const lowStock = [];3 4 for (const product of products) {5 "cmt">// Check each channel6 for (const channel of product.channels) {7 if (channel.severity === 'LOW' || channel.severity === 'CRITICAL') {8 lowStock.push({9 productMasterId: product.productMasterId,10 productName: product.productName,11 sku: product.globalSku,12 channelId: channel.channelId,13 channelName: channel.channelName,14 atp: channel.atp,15 threshold: channel.threshold,16 severity: channel.severity,17 onHand: channel.onHand,18 reserved: channel.reserved19 });20 }21 }22 23 "cmt">// Also check overall minimum ATP24 if (product.minAtp <= 0) {25 lowStock.push({26 productMasterId: product.productMasterId,27 productName: product.productName,28 sku: product.globalSku,29 channelId: 'ALL',30 channelName: 'All Channels',31 atp: product.minAtp,32 severity: 'CRITICAL',33 onHand: product.totalOnHand,34 reserved: product.totalReserved35 });36 }37 }38 39 return lowStock;40}For low-stock items, get detailed inventory information.
Request:
1curl -X GET "https://app.betterdata.co/api/inventory?locationId=loc_123&productMasterId=prod_123" \2 -H "Authorization: Bearer YOUR_API_KEY" \3 -H "Content-Type: application/json"Response:
1{2 "items": [3 {4 "id": "item_123",5 "productMasterId": "prod_123",6 "locationId": "loc_123",7 "quantityOnHand": 200,8 "quantityReserved": 50,9 "quantityAvailable": 150,10 "lotId": "lot_123",11 "expiryDate": "2024-12-31",12 "binId": "bin_123"13 }14 ],15 "pagination": {16 "page": 1,17 "pageSize": 20,18 "total": 1,19 "totalPages": 120 }21}Create a monitoring script that periodically checks ATP levels.
Example Script:
1#!/bin/bash2 3API_KEY="YOUR_API_KEY"4BASE_URL="https://app.betterdata.co/api"5LOCATION_ID="loc_123"6ALERT_THRESHOLD=10 # Alert if ATP < 107 8# Function to check ATP and alert on low stock9check_atp() {10 PAGE=111 LOW_STOCK_COUNT=012 13 while true; do14 RESPONSE=$(curl -s -X GET "${BASE_URL}/inventory/channel-location?locationId=${LOCATION_ID}&page=${PAGE}&limit=100" \15 -H "Authorization: Bearer ${API_KEY}" \16 -H "Content-Type: application/json")17 18 PRODUCTS=$(echo $RESPONSE | jq -r '.products[]')19 TOTAL_PAGES=$(echo $RESPONSE | jq -r '.pagination.totalPages')20 21 # Process each product22 echo $PRODUCTS | jq -c '.' | while read product; do23 PRODUCT_NAME=$(echo $product | jq -r '.productName')24 SKU=$(echo $product | jq -r '.globalSku')25 MIN_ATP=$(echo $product | jq -r '.minAtp')26 HAS_CRITICAL=$(echo $product | jq -r '.hasCritical')27 28 if [ "$HAS_CRITICAL" = "true" ] || [ $(echo "$MIN_ATP < $ALERT_THRESHOLD" | bc) -eq 1 ]; then29 echo "ALERT: Low stock for ${PRODUCT_NAME} (${SKU}) - ATP: ${MIN_ATP}"30 LOW_STOCK_COUNT=$((LOW_STOCK_COUNT + 1))31 fi32 done33 34 # Check if more pages35 if [ $PAGE -ge $TOTAL_PAGES ]; then36 break37 fi38 PAGE=$((PAGE + 1))39 done40 41 echo "Found ${LOW_STOCK_COUNT} low-stock items"42}43 44# Run check every 5 minutes45while true; do46 check_atp47 sleep 300 # 5 minutes48doneHere's a complete Python example for ATP monitoring:
1import requests2import time3from datetime import datetime4 5API_KEY = "YOUR_API_KEY"6BASE_URL = "https://app.betterdata.co/api"7LOCATION_ID = "loc_123"8ALERT_THRESHOLD = 109 10def get_atp_matrix(location_id, page=1, limit=100):11 """Fetch ATP matrix for a location."""12 url = f"{BASE_URL}/inventory/channel-location"13 headers = {14 "Authorization": f"Bearer {API_KEY}",15 "Content-Type": "application/json"16 }17 params = {18 "locationId": location_id,19 "page": page,20 "limit": limit21 }22 23 response = requests.get(url, headers=headers, params=params)24 response.raise_for_status()25 return response.json()26 27def identify_low_stock(products, threshold):28 """Identify products with low ATP."""29 low_stock = []30 31 for product in products:32 # Check minimum ATP across all channels33 if product["minAtp"] < threshold or product["hasCritical"]:34 low_stock.append({35 "productMasterId": product["productMasterId"],36 "productName": product["productName"],37 "sku": product["globalSku"],38 "minAtp": product["minAtp"],39 "maxAtp": product["maxAtp"],40 "totalOnHand": product["totalOnHand"],41 "totalReserved": product["totalReserved"],42 "channels": [43 {44 "channelId": ch["channelId"],45 "channelName": ch["channelName"],46 "atp": ch["atp"],47 "severity": ch["severity"]48 }49 for ch in product["channels"]50 if ch["severity"] in ["LOW", "CRITICAL"]51 ]52 })53 54 return low_stock55 56def monitor_atp(location_id, threshold, interval_seconds=300):57 """Monitor ATP levels and alert on low stock."""58 print(f"Starting ATP monitoring for location {location_id}")59 print(f"Alert threshold: {threshold}")60 print(f"Check interval: {interval_seconds} seconds")61 print("-" * 50)62 63 while True:64 try:65 # Fetch all pages66 all_products = []67 page = 168 69 while True:70 data = get_atp_matrix(location_id, page=page)71 all_products.extend(data["products"])72 73 total_pages = data["pagination"]["totalPages"]74 if page >= total_pages:75 break76 page += 177 78 # Identify low stock79 low_stock = identify_low_stock(all_products, threshold)80 81 # Report82 timestamp = datetime.now().isoformat()83 print(f"\n[{timestamp}] ATP Check Complete")84 print(f"Total products: {len(all_products)}")85 print(f"Low stock items: {len(low_stock)}")86 87 if low_stock:88 print("\nLow Stock Alerts:")89 for item in low_stock:90 print(f" - {item['productName']} ({item['sku']})")91 print(f" Min ATP: {item['minAtp']}, On Hand: {item['totalOnHand']}")92 for channel in item['channels']:93 print(f" {channel['channelName']}: ATP={channel['atp']} ({channel['severity']})")94 95 # Wait before next check96 time.sleep(interval_seconds)97 98 except Exception as e:99 print(f"Error during ATP check: {e}")100 time.sleep(60) # Wait 1 minute before retry101 102if __name__ == "__main__":103 monitor_atp(LOCATION_ID, ALERT_THRESHOLD, interval_seconds=300)page and limit (default: 50, max: 200).page and limit (default: 20, max: 100).Symptom: ATP values don't match expected calculations.
Solutions:
Symptom: Some products don't appear in ATP matrix.
Solutions:
Symptom: Severity remains "OK" despite low ATP.
Solutions:
ATP monitoring requires inventory.read permission. No special roles required beyond basic authentication.
