API Documentation
Everything you need to integrate nanocart into your site.
https://api.nanocart.io
Authentication
Public endpoints (products, categories, checkout) require no authentication. They're meant to be called from your storefront.
Admin endpoints require an API key passed in the x-api-key header. You can find your API key in the admin panel under Settings.
bash
Copy
curl https://api.nanocart.io/shop/my-store/admin/products \
-H "x-api-key: sc_live_your_api_key_here"
API Key Format: sc_live_ followed by 64 hex characters. Keep your API key secret — it provides full admin access to your store.
Conventions
Prices
All monetary values are in cents (integers). Divide by 100 for display.
API Value Display
999 $9.99
2800 $28.00
0 $0.00 (free)
Pagination
List endpoints return a lastKey field. If non-null, pass it as a query parameter to get the next page:
Copy
GET /shop/my-store/products?limit=20&lastKey=eyJzdG9yZUlkIjoi...
Dates
All timestamps are ISO 8601 UTC strings: 2026-02-24T15:30:00Z
Response Format
All responses return JSON with appropriate HTTP status codes. Error responses include an error field and optional code field.
Service Tiers
Each store has a tier that determines limits. Upgrade from the admin panel or via the subscription API.
Limit Free Standard Pro Expert
Price $0 $5/mo $10/mo $25/mo
Active Products 5 25 100 Unlimited
Monthly Orders 5 500 5,000 Unlimited
Coupons 1 5 10 Unlimited
File Upload 5 MB 25 MB 50 MB 500 MB
Shipping Methods Flat rate, Free All All All
Local Pickup No Yes Yes Yes
Public API
These endpoints require no authentication. Call them from your storefront to display products and process orders.
List active products with optional filtering, sorting, and pagination.
Query Parameters
Parameter Type Default Description
category string — Filter by category slug
sort string newest newest, price, or featured
limit integer 50 Max results per page (max 50)
lastKey string — Pagination token from previous response
Response
JSON
Copy
{
"products": [
{
"productId": "dd9392ed-ea5a-494c-adbc-a8ce5288c2e8",
"slug": "trucker-surf-hat",
"name": "Trucker Surf Hat",
"price": 2800,
"compareAtPrice": null,
"images": ["https://images.unsplash.com/photo-..."],
"variants": [
{
"variantId": "d90da05a",
"name": "Adjustable / Navy",
"price": 2800,
"sku": "TSH-ADJ-NVY"
}
],
"featured": false,
"categoryId": "abc-123",
"inventory": 50,
"productType": "physical",
"status": "active",
"createdAt": "2026-02-23T20:59:28Z",
"updatedAt": "2026-02-23T20:59:28Z"
}
],
"lastKey": null
}
Get full details for a single product by slug.
Response
JSON
Copy
{
"product": {
"productId": "dd9392ed-ea5a-494c-adbc-a8ce5288c2e8",
"slug": "trucker-surf-hat",
"name": "Trucker Surf Hat",
"description": "Classic trucker hat with embroidered surf logo.",
"price": 2800,
"compareAtPrice": null,
"categoryId": "abc-123",
"images": ["https://images.unsplash.com/photo-..."],
"variants": [
{
"variantId": "d90da05a",
"name": "Adjustable / Navy",
"price": 2800,
"sku": "TSH-ADJ-NVY",
"inventory": 25
}
],
"options": [
{ "name": "Size", "values": ["Adjustable"] },
{ "name": "Color", "values": ["Navy", "Black"] }
],
"inventory": 50,
"productType": "physical",
"shippingRequired": true,
"taxable": true,
"tags": ["hats", "surf"],
"featured": false,
"status": "active",
"createdAt": "2026-02-23T20:59:28Z",
"updatedAt": "2026-02-23T20:59:28Z"
}
}
Errors
Status Response
404 {"error": "Product not found"}
List all active categories for a store, sorted by sortOrder. Categories support a parent/child hierarchy via the parentId field. Top-level categories have an empty parentId. Subcategories reference their parent's categoryId.
Response
JSON
Copy
{
"categories": [
{
"categoryId": "cat-001",
"name": "Men's",
"slug": "mens",
"description": "Men's clothing and accessories",
"image": "https://...",
"parentId": "",
"sortOrder": 0,
"status": "active"
},
{
"categoryId": "cat-002",
"name": "Shirts",
"slug": "shirts",
"description": "Men's shirts",
"image": "https://...",
"parentId": "cat-001",
"sortOrder": 0,
"status": "active"
},
{
"categoryId": "cat-003",
"name": "Pants",
"slug": "pants",
"description": "",
"image": "",
"parentId": "cat-001",
"sortOrder": 1,
"status": "active"
}
]
}
Category Fields
Field Type Description
categoryId string Unique category identifier
name string Category display name
slug string URL-friendly name (unique within store)
description string Optional description
image string Optional image URL (for category cards)
parentId string Empty string for top-level categories. Set to a parent categoryId to make this a subcategory.
sortOrder number Display order (lower = first)
status string active or hidden
Subcategory pattern: To build a hierarchical category tree, filter categories where parentId is empty to get top-level parents, then filter where parentId === parent.categoryId to get children. Products are assigned to a single categoryId — when displaying a parent category, you may want to also include products from its subcategories.
Validate a coupon code and calculate the discount amount.
Request Body
JSON
Copy
{
"code": "SAVE20",
"subtotal": 4999
}
Response (valid)
JSON
Copy
{
"valid": true,
"type": "percent_off",
"value": 20,
"discountAmount": 999,
"message": "20% off applied!"
}
Response (invalid)
JSON
{
"valid": false,
"message": "This coupon has expired"
}
Coupon Types: percent_off, fixed_amount, free_shipping
Create a Stripe Checkout Session. Validates inventory, applies coupons, calculates shipping and tax, and returns a Stripe payment URL.
Request Body
JSON
Copy
{
"items": [
{
"productId": "dd9392ed-ea5a-494c-adbc-a8ce5288c2e8",
"variantId": "d90da05a",
"quantity": 2
}
],
"email": "customer@example.com",
"couponCode": "SAVE20",
"shippingMethod": "standard"
}
Field Type Required Description
items array required Cart items with productId, optional variantId, and quantity
email string required Customer email for order confirmation
couponCode string optional Coupon code to apply
shippingMethod string optional standard (default) or local_pickup
Response
JSON
{
"sessionUrl": "https://checkout.stripe.com/c/pay/cs_live_...",
"orderId": "a1b2c3d4-e5f6-..."
}
Redirect the customer to sessionUrl to complete payment on Stripe.
Errors
Status Error
400 Cart is empty
400 Email address is required for checkout.
400 Product {id} is not available
400 {item} is out of stock
403 This store has reached its monthly order limit
Look up an order by ID. Requires the customer's email for verification.
Query Parameters
Parameter Type Required Description
email string required Must match the order's email
Response
JSON
Copy
{
"order": {
"orderId": "a1b2c3d4-e5f6-...",
"orderNumber": "MYS-1001",
"email": "customer@example.com",
"customerName": "John Doe",
"items": [
{
"productId": "dd9392ed-...",
"name": "Trucker Surf Hat",
"variantName": "Adjustable / Navy",
"price": 2800,
"quantity": 2
}
],
"subtotal": 5600,
"shippingCost": 499,
"shippingMethod": "standard",
"taxAmount": 336,
"discountAmount": 0,
"total": 6435,
"shippingAddress": {
"name": "John Doe",
"line1": "123 Main St",
"city": "Charleston",
"state": "SC",
"zip": "29401",
"country": "US"
},
"status": "paid",
"createdAt": "2026-02-24T12:00:00Z"
}
}
Get the storefront configuration for a hosted store. Used by the storefront SPA on page load to get template, branding, and content settings. Only works for stores with an active hosted plan.
Response
JSON
Copy
{
"storeId": "my-store",
"storeName": "My Store",
"currency": "usd",
"template": "classic",
"accentColor": "#418a9e",
"backgroundColor": "#FFFFFF",
"textColor": "#333333",
"storeDescription": "Your store description here",
"aboutContent": "About page content...",
"aboutTitle": "About My Store",
"aboutImage": "https://...",
"heroTitle": "Welcome to My Store",
"heroLayout": "center",
"heroImage": "",
"logo": "https://...",
"showStoreName": true,
"favicon": "",
"announcementBar": {
"enabled": false,
"text": "",
"backgroundColor": "#418a9e",
"textColor": "#FFFFFF"
},
"socialLinks": {
"instagram": "",
"twitter": "",
"facebook": "",
"tiktok": "",
"youtube": ""
},
"showPoweredBy": true
}
Admin API
All admin endpoints require authentication via the x-api-key header.
Stores
List all stores you have access to.
Response
JSON
Copy
{
"stores": [
{
"storeId": "my-store",
"name": "My Store",
"tier": "standard",
"status": "active",
"domain": "https://mystore.com",
"contactEmail": "me@mystore.com",
"currency": "usd",
"brandColor": "#418a9e",
"apiKey": "sc_live_...",
"createdAt": "2026-02-20T10:00:00Z"
}
]
}
Create a new store. Returns the store details and API key.
Request Body
JSON
Copy
{
"storeId": "my-store",
"name": "My Store",
"domain": "https://mystore.com",
"contactEmail": "me@mystore.com",
"stripePublishableKey": "pk_live_...",
"stripeSecretKey": "sk_live_...",
"stripeWebhookSecret": "whsec_...",
"brandColor": "#418a9e"
}
Field Type Required Description
storeId string required 3-32 chars, lowercase alphanumeric + hyphens
name string optional Display name
domain string optional Your storefront URL
contactEmail string optional Store contact email
stripePublishableKey string optional Stripe publishable key (pk_live_ or pk_test_)
stripeSecretKey string optional Stripe secret key (sk_live_ or sk_test_)
stripeWebhookSecret string optional Stripe webhook secret (whsec_)
brandColor string optional Hex color for widget branding
Response (201)
JSON
{
"store": { ... },
"apiKey": "sc_live_abc123...",
"message": "Store created successfully. Save your API key."
}
Update store settings. Only include fields you want to change.
Request Body
JSON
Copy
{
"name": "New Store Name",
"domain": "https://newdomain.com",
"brandColor": "#4292e7",
"allowedDomains": ["mystore.com", "www.mystore.com"]
}
Allowed Domains: If set, only these domains can call your public API. Max 10 domains. Leave empty to allow all origins.
Regenerate your API key. The old key becomes invalid immediately.
Response
JSON
{
"apiKey": "sc_live_new_key_here...",
"message": "API key regenerated. Old key is now invalid."
}
Products
Create a new product.
Request Body
JSON
Copy
{
"name": "Trucker Surf Hat",
"price": 2800,
"description": "Classic trucker hat with embroidered surf logo.",
"categoryId": "abc-123",
"compareAtPrice": 3500,
"inventory": 50,
"images": ["https://..."],
"variants": [
{
"variantId": "d90da05a",
"name": "Adjustable / Navy",
"price": 2800,
"sku": "TSH-ADJ-NVY",
"inventory": 25
}
],
"options": [
{ "name": "Size", "values": ["Adjustable"] },
{ "name": "Color", "values": ["Navy", "Black"] }
],
"productType": "physical",
"taxable": true,
"tags": ["hats"],
"status": "active",
"featured": false
}
Field Type Required Description
name string required Product name
price integer required Price in cents
description string optional HTML description
categoryId string optional Category UUID
compareAtPrice integer optional Original price for sale display (must be > price)
inventory integer optional Stock quantity. Null = unlimited.
images array optional Array of image URLs
variants array optional Product variants (variantId, name, price, sku, inventory)
options array optional Option definitions for variant generation
productType string optional physical (default) or digital
status string optional draft (default), active
featured boolean optional Featured product flag
slug string optional URL slug. Auto-generated from name if omitted.
taxable boolean optional Subject to tax (default true)
tags array optional String tags for organization
shippingCost integer optional Per-item shipping cost in cents (for per_item method)
Response (201)
JSON
{
"product": {
"productId": "dd9392ed-...",
"slug": "trucker-surf-hat",
"name": "Trucker Surf Hat",
...
}
}
Update an existing product. Only include fields you want to change.
Request Body
JSON
Copy
{
"productId": "dd9392ed-ea5a-494c-adbc-a8ce5288c2e8",
"price": 3200,
"inventory": 40,
"status": "active"
}
Field Type Required Description
productId string required Product UUID to update
All other fields from Create are accepted. Only provided fields are updated.
Archive a product. It remains in the database but is hidden from public listings.
Request Body
JSON
Copy
{
"productId": "dd9392ed-ea5a-494c-adbc-a8ce5288c2e8"
}
Categories
Create a new category. Set parentId to create a subcategory under an existing parent category.
Request Body
Field Type Required Description
name string required Category name
description string optional Category description
image string optional Image URL (use the upload endpoint to get a URL first)
parentId string optional Set to a parent categoryId to make this a subcategory. Omit or send empty string for top-level.
sortOrder number optional Display order (default: 0)
status string optional active (default) or hidden
Example: Top-level category
JSON
Copy
{
"name": "Men's",
"description": "Men's clothing and accessories",
"image": "https://...",
"sortOrder": 0
}
Example: Subcategory
JSON
Copy
{
"name": "Shirts",
"description": "Men's shirts",
"parentId": "cat-001",
"image": "https://...",
"sortOrder": 0
}
Update a category. Only include the fields you want to change (plus categoryId which is always required).
Updatable Fields
name, slug, description, image, parentId, sortOrder, status
Example: Move to subcategory and update sort order
JSON
Copy
{
"categoryId": "cat-002",
"parentId": "cat-001",
"sortOrder": 2
}
Example: Promote to top-level
JSON
Copy
{
"categoryId": "cat-002",
"parentId": ""
}
Hide a category. Sets status to "hidden". Note: hiding a parent category does not automatically hide its subcategories.
JSON
Copy
{
"categoryId": "abc-123"
}
Coupons
List all coupons for this store.
JSON
Copy
{
"coupons": [
{
"code": "SAVE20",
"type": "percent_off",
"value": 20,
"minOrderAmount": 5000,
"maxUses": 100,
"currentUses": 42,
"expiresAt": "2026-12-31T23:59:59Z",
"status": "active"
}
]
}
Create a new coupon.
JSON
Copy
{
"code": "SAVE20",
"type": "percent_off",
"value": 20,
"minOrderAmount": 5000,
"maxUses": 100,
"expiresAt": "2026-12-31T23:59:59Z",
"status": "active"
}
Field Type Required Description
code string required Coupon code (auto-uppercased)
type string required percent_off, fixed_amount, or free_shipping
value integer required Percentage (1-100) or cents amount
minOrderAmount integer optional Minimum subtotal in cents
maxUses integer optional Max number of times coupon can be used
expiresAt string optional ISO datetime when coupon expires
Update a coupon. Code is required to identify which coupon to update.
JSON
Copy
{
"code": "SAVE20",
"value": 25,
"maxUses": 200
}
Deactivate a coupon.
JSON
{
"code": "SAVE20"
}
Orders
List orders with optional filtering.
Query Parameters
Parameter Type Description
status string Filter by status (e.g. paid, shipped)
from string ISO datetime to filter from
to string ISO datetime to filter to
limit integer Results per page (default 50)
lastKey string Pagination token
Response
JSON
Copy
{
"orders": [
{
"orderId": "a1b2c3d4-...",
"orderNumber": "MYS-1001",
"email": "customer@example.com",
"customerName": "John Doe",
"items": [...],
"subtotal": 5600,
"shippingCost": 499,
"taxAmount": 336,
"discountAmount": 0,
"total": 6435,
"status": "paid",
"trackingNumber": null,
"createdAt": "2026-02-24T12:00:00Z"
}
],
"lastKey": null
}
Update order status, tracking number, or notes.
JSON
Copy
{
"status": "shipped",
"trackingNumber": "1Z999AA10123456784",
"notes": "Shipped via UPS"
}
Settings
Get all store settings (tax, shipping, email, order config).
JSON
Copy
{
"settings": {
"tax_config": {
"enabled": true,
"defaultRate": 0.06,
"stateRates": { "SC": 0.06, "NC": 0.07, "NY": 0.08 }
},
"shipping_config": {
"method": "flat_rate",
"flatRate": 499,
"freeShippingEnabled": false,
"freeShippingThreshold": 5000,
"localPickupEnabled": false
},
"email_config": {
"fromEmail": "noreply@mystore.com",
"fromName": "My Store"
},
"order_config": {
"orderPrefix": "MYS",
"nextOrderNumber": 1001
}
}
}
Update a specific setting.
JSON
Copy
{
"settingKey": "shipping_config",
"value": {
"method": "flat_rate",
"flatRate": 599,
"freeShippingEnabled": true,
"freeShippingThreshold": 7500,
"localPickupEnabled": true,
"localPickupInstructions": "Pick up at 123 Main St, 9AM-5PM"
}
}
Setting Key Description
tax_config Tax rates (default rate + state-specific rates)
shipping_config Shipping method, rates, free shipping, local pickup
email_config From email address and name for order emails
order_config Order number prefix and next number
Shipping Methods: flat_rate (single rate), per_item (per product), tiered (by subtotal), free. Free tier stores are limited to flat_rate and free.
Reports
Get sales reports with revenue, order counts, top products, and tax breakdown.
Query Parameters
Parameter Type Default Description
from string 2020-01-01 Start date (ISO)
to string now End date (ISO)
status string paid,shipped,delivered Comma-separated order statuses to include
Response
JSON
Copy
{
"period": { "from": "2026-02-01", "to": "2026-02-28" },
"totalRevenue": 523400,
"totalOrders": 156,
"averageOrderValue": 3355,
"totalTaxCollected": 28450,
"taxByState": { "SC": 18900, "NC": 6200 },
"totalShipping": 45200,
"totalDiscounts": 12300,
"topProducts": [
{ "productId": "...", "name": "Trucker Hat", "unitsSold": 156, "revenue": 155844 }
]
}
Uploads
Get a presigned S3 URL for uploading images or digital files. Use the returned fileUrl when creating/updating products or categories.
Request Body
Field Type Required Description
fileName string required Original file name
contentType string required MIME type (e.g. image/jpeg, image/png)
uploadType string required product_image, digital_file, category_image, or storefront_image
fileSizeMB number required File size in megabytes (validated against tier limits)
Upload Types & S3 Paths
uploadType S3 Path Use For
product_image{storeId}/products/{file}Product images array
digital_file{storeId}/digital/{file}Downloadable digital products
category_image{storeId}/categories/{file}Category/subcategory card images
storefront_image{storeId}/storefront/{file}Hero images, about page images, logo
Example
JSON
Copy
{
"fileName": "product-photo.jpg",
"contentType": "image/jpeg",
"uploadType": "product_image",
"fileSizeMB": 2.5
}
Response
JSON
{
"uploadUrl": "https://shoppingcart-assets.s3.amazonaws.com/...?X-Amz-Signature=...",
"fileUrl": "https://shoppingcart-assets.s3.amazonaws.com/my-store/products/abc.jpg",
"s3Key": "my-store/products/abc.jpg"
}
Upload your file with a PUT request to uploadUrl with the Content-Type header matching what you specified. Then use fileUrl in your product images array, category image field, etc.
Storefront
Get the current storefront settings for your store.
Response
JSON
Copy
{
"storefront": {
"enabled": true,
"template": "classic",
"logo": "https://...",
"showStoreName": true,
"accentColor": "#418a9e",
"backgroundColor": "#FFFFFF",
"textColor": "#333333",
"heroTitle": "Welcome to My Store",
"heroLayout": "center",
"heroImage": "",
"storeDescription": "Your store description",
"aboutTitle": "About My Store",
"aboutContent": "About page content...",
"aboutImage": "",
"announcementBar": {
"enabled": false,
"text": "",
"backgroundColor": "#418a9e",
"textColor": "#FFFFFF"
},
"socialLinks": {
"instagram": "",
"twitter": "",
"facebook": "",
"tiktok": "",
"youtube": ""
},
"showPoweredBy": true
}
}
Update storefront settings. Only include the fields you want to change. Requires an active hosted plan.
Updatable Fields
Field Type Description
template string Template name: classic, bold, compact
logo string Store logo URL (recommended 240×72px). Use upload endpoint with storefront_image type.
showStoreName boolean Show store name text next to logo in nav (default: true)
accentColor string Primary accent color hex (e.g. #418a9e)
backgroundColor string Page background color hex
textColor string Body text color hex
heroTitle string Hero section heading (defaults to store name)
heroLayout string center (default), image-left, or image-right
heroImage string Hero image URL (used with image-left/image-right layouts)
storeDescription string Hero description text (displayed below title)
aboutTitle string About page heading (defaults to "About {storeName}")
aboutContent string About page body content (line breaks preserved)
aboutImage string About page image URL
announcementBar object { enabled, text, backgroundColor, textColor }
socialLinks object { instagram, twitter, facebook, tiktok, youtube }
showPoweredBy boolean Show "Powered by Nanocart" in footer
favicon string Favicon URL
Example
JSON
Copy
{
"heroTitle": "Welcome to My Shop",
"heroLayout": "image-right",
"heroImage": "https://...",
"accentColor": "#e74c3c",
"aboutTitle": "Our Story"
}
Tier & Usage
Get current tier info, limits, and usage.
JSON
Copy
{
"tier": "standard",
"limits": {
"maxProducts": 25,
"maxMonthlyOrders": 500,
"maxCoupons": 5,
"maxUploadMB": 25,
"allowedShippingMethods": ["flat_rate", "per_item", "tiered", "free"],
"localPickup": true
},
"usage": {
"activeProducts": 12,
"activeCoupons": 2,
"monthlyOrders": 47
}
}
Subscriptions
Create a Stripe subscription checkout session to upgrade your plan.
JSON
Copy
{
"tier": "pro",
"billingPeriod": "monthly"
}
Field Type Options
tier string standard, pro, expert
billingPeriod string monthly, annual
Response
JSON
{
"sessionUrl": "https://checkout.stripe.com/c/pay/..."
}
Get current subscription details.
JSON
{
"tier": "pro",
"subscriptionId": "sub_...",
"subscriptionStatus": "active",
"billingPeriod": "monthly",
"cancelAtPeriodEnd": false
}
Cancel subscription at end of billing period. Store keeps current tier until then.
JSON
{
"message": "Subscription will cancel at end of billing period."
}
Error Codes
Error responses include an error message and an optional code for programmatic handling:
JSON
{
"error": "Free plan allows 5 active products. Upgrade to Standard for 25.",
"code": "TIER_PRODUCT_LIMIT",
"tierLimit": true
}
Code HTTP Description
INVALID_API_KEY 401 API key doesn't exist or is invalid
STORE_SUSPENDED 403 Store has been suspended
INVALID_INPUT 400 Missing or malformed request data
STRIPE_NOT_CONFIGURED 400 Store has no Stripe keys set up
STRIPE_INVALID_KEYS 400 Stripe key format is wrong
STRIPE_AUTH_ERROR 400 Stripe rejected the keys
DOMAIN_NOT_ALLOWED 403 Origin domain not in allowedDomains
TIER_PRODUCT_LIMIT 403 Exceeded active product limit for tier
TIER_ORDER_LIMIT 403 Exceeded monthly order limit for tier
TIER_COUPON_LIMIT 403 Exceeded coupon limit for tier
TIER_UPLOAD_LIMIT 403 File size exceeds tier upload limit
TIER_SHIPPING_RESTRICTED 403 Shipping method not available on tier
DUPLICATE_COUPON 409 Coupon code already exists
Tier limit errors include "tierLimit": true so your app can detect upgrade prompts. The error message includes the current limit and the next tier's limit.