Skip to main content
RapidDev - Software Development Agency
replit-integrationsStandard API Integration

How to Integrate Replit with 3dcart (now Shift4Shop)

To integrate Replit with 3dcart (now Shift4Shop), generate a REST API token from your Shift4Shop store settings, store it in Replit Secrets (lock icon πŸ”’), and call the Shift4Shop REST API from Python or Node.js to manage products, orders, and customers. Shift4Shop's free enterprise plan with Shift4 payment processing makes it a compelling hosted e-commerce backend for Replit-built storefronts.

What you'll learn

  • How to generate a Shift4Shop REST API token and store it in Replit Secrets
  • How to retrieve and create products, orders, and customers using Python and Node.js
  • How to build a server-side proxy to keep your store API token secure
  • How to handle Shift4Shop webhook notifications for new order events
  • How to deploy your Replit integration for production order processing
Book a free consultation
4.9Clutch rating ⭐
600+Happy partners
17+Countries served
190+Team members
Intermediate15 min read30 minutesE-commerceMarch 2026RapidDev Engineering Team
TL;DR

To integrate Replit with 3dcart (now Shift4Shop), generate a REST API token from your Shift4Shop store settings, store it in Replit Secrets (lock icon πŸ”’), and call the Shift4Shop REST API from Python or Node.js to manage products, orders, and customers. Shift4Shop's free enterprise plan with Shift4 payment processing makes it a compelling hosted e-commerce backend for Replit-built storefronts.

Why Connect Replit to Shift4Shop?

Shift4Shop (the platform formerly known as 3dcart) offers one of the few genuinely free enterprise-tier e-commerce plans β€” available at no monthly cost when you use Shift4 as your payment processor. Its REST API v3 gives you programmatic access to every aspect of your store: products, categories, orders, customers, inventory, and shipping. This makes it an attractive backend for Replit developers building custom storefronts, order management dashboards, or fulfilment automation.

Common integration patterns include syncing Shift4Shop orders to an external database or fulfilment system, automating product imports from a supplier feed, building a custom checkout flow that calls the Shift4Shop API behind the scenes, and generating business analytics from raw order data. Replit's always-on deployment options mean your integration can respond to real-time webhook events without polling.

Because the Shift4Shop API token grants full access to your store data β€” including customer PII and order financials β€” it must be stored in Replit Secrets (lock icon πŸ”’ in the sidebar) and accessed only from server-side code. Never expose this token to browser clients or commit it to your Replit Git repository.

Integration method

Standard API Integration

You connect Replit to Shift4Shop by generating a REST API token from your store's API settings, storing it in Replit Secrets, and calling the Shift4Shop REST API v3 from your server-side Python or Node.js code. The API supports products, orders, customers, categories, and inventory with standard CRUD operations. Webhooks can notify your Replit app of new orders and inventory changes in real time.

Prerequisites

  • A Replit account with a Python or Node.js project created
  • A Shift4Shop (3dcart) store with API access enabled (requires a paid legacy plan or the free enterprise plan with Shift4 processing)
  • Your Shift4Shop store URL (e.g., https://www.yourstore.com) and REST API token
  • Basic familiarity with REST APIs and Bearer token authentication
  • Node.js 18+ or Python 3.10+ (both available on Replit by default)

Step-by-step guide

1

Generate a Shift4Shop REST API Token

Log in to your Shift4Shop store manager and navigate to Settings in the left sidebar, then select API. On the API Settings page you will see an option to generate or manage REST API tokens. Click 'Generate Token' to create a new API key. The token is a long alphanumeric string β€” copy it immediately as it may only be shown once. Also note your store's base URL (e.g., https://www.yourstore.com). This is used as part of all API endpoint calls. Some older 3dcart accounts may still use the 3dcart API domain β€” check the API Settings page for the correct endpoint URL. Shift4Shop REST API access requires either a legacy paid 3dcart plan or the free enterprise plan available with Shift4 as your payment processor. If you are on the free plan, confirm that API access is listed as an included feature in your plan details. For security, generate a token specifically for your Replit integration and label it accordingly. This lets you revoke access later without affecting other integrations. The REST API token provides full store access including reading customer data, so treat it like a production database password.

Pro tip: Shift4Shop also has a legacy SOAP API β€” use the REST API v3 for all new integrations. REST endpoints are under /api.php?Route=... and return JSON, while the SOAP API uses XML and is being phased out.

Expected result: You have a Shift4Shop REST API token and your store URL copied and ready to add to Replit Secrets.

2

Store Credentials in Replit Secrets

Open your Replit project and click the lock icon πŸ”’ in the left sidebar to open the Secrets pane. Add the following secrets: - Key: SHIFT4SHOP_API_TOKEN β€” Value: your REST API token - Key: SHIFT4SHOP_STORE_URL β€” Value: your store base URL (e.g., https://www.yourstore.com) Click 'Add Secret' after each entry. Replit stores these values encrypted at rest and injects them as environment variables when your code runs. They never appear in your file tree or Git history. In Python, access these with os.environ['SHIFT4SHOP_API_TOKEN']. In Node.js, use process.env.SHIFT4SHOP_API_TOKEN. Never hardcode the token in your source files β€” Replit's Secret Scanner will flag exposed API keys and alert you. If your store uses a custom domain with HTTPS, make sure the SHIFT4SHOP_STORE_URL includes the https:// prefix and no trailing slash. The API endpoints are constructed as STORE_URL + '/api.php?Route=Orders&CountOnly=y' and similar patterns.

Pro tip: Add a third secret SHIFT4SHOP_PRIVATE_KEY if you are using the v2 API authentication which requires both a public token and a private key β€” check your API Settings page for which version your store uses.

Expected result: SHIFT4SHOP_API_TOKEN and SHIFT4SHOP_STORE_URL appear in the Replit Secrets pane with masked values.

3

Query and Manage Store Data with Python

The Shift4Shop REST API uses Bearer token authentication. All requests include an Authorization header with your API token and a PrivateKey header if required by your store configuration. The API base URL pattern is your store URL followed by /api.php with a Route query parameter specifying the resource. The Python code below implements a client for the most common operations: listing orders, fetching a single order by ID, listing products, updating product inventory, and creating customers. The requests library is pre-installed on Replit and handles all HTTP operations. Note that Shift4Shop API responses wrap data in a consistent envelope β€” most list endpoints return an array of objects directly, while single-record endpoints return the object. Always check the HTTP status code and look for error fields in the response body, as the API sometimes returns 200 with an error message in the JSON payload.

shift4shop_client.py
1import os
2import requests
3from typing import Optional
4
5API_TOKEN = os.environ["SHIFT4SHOP_API_TOKEN"]
6STORE_URL = os.environ["SHIFT4SHOP_STORE_URL"].rstrip("/")
7BASE_URL = f"{STORE_URL}/api.php"
8
9HEADERS = {
10 "Authorization": f"Bearer {API_TOKEN}",
11 "Content-Type": "application/json",
12 "Accept": "application/json"
13}
14
15def get_orders(status: Optional[str] = None, limit: int = 20) -> list:
16 """Fetch orders, optionally filtered by status (e.g., 'New', 'Processing', 'Shipped')."""
17 params = {"Route": "Orders", "limit": limit, "offset": 0}
18 if status:
19 params["Status"] = status
20 response = requests.get(BASE_URL, headers=HEADERS, params=params)
21 response.raise_for_status()
22 return response.json()
23
24def get_order(order_id: str) -> dict:
25 """Fetch a single order by order ID."""
26 params = {"Route": f"Orders/{order_id}"}
27 response = requests.get(BASE_URL, headers=HEADERS, params=params)
28 response.raise_for_status()
29 return response.json()
30
31def update_order_status(order_id: str, status: str) -> dict:
32 """Update the status of an order (e.g., 'Processing', 'Shipped', 'Delivered')."""
33 params = {"Route": f"Orders/{order_id}"}
34 data = {"OrderStatus": status}
35 response = requests.put(BASE_URL, headers=HEADERS, params=params, json=data)
36 response.raise_for_status()
37 return response.json()
38
39def get_products(limit: int = 50) -> list:
40 """List all products in the store."""
41 params = {"Route": "Products", "limit": limit, "offset": 0}
42 response = requests.get(BASE_URL, headers=HEADERS, params=params)
43 response.raise_for_status()
44 return response.json()
45
46def update_product_stock(catalog_id: str, new_stock: int) -> dict:
47 """Update the stock level for a product by CatalogID."""
48 params = {"Route": f"Products/{catalog_id}"}
49 data = {"Stock": new_stock}
50 response = requests.put(BASE_URL, headers=HEADERS, params=params, json=data)
51 response.raise_for_status()
52 return response.json()
53
54def get_customers(limit: int = 50) -> list:
55 """List customers from the store."""
56 params = {"Route": "Customers", "limit": limit, "offset": 0}
57 response = requests.get(BASE_URL, headers=HEADERS, params=params)
58 response.raise_for_status()
59 return response.json()
60
61if __name__ == "__main__":
62 # Fetch recent new orders
63 orders = get_orders(status="New", limit=5)
64 print(f"New orders: {len(orders)}")
65 for order in orders:
66 print(f" Order {order.get('OrderID')} β€” ${order.get('OrderAmount')} β€” {order.get('BillingFirstName')} {order.get('BillingLastName')}")
67
68 # List first 5 products with stock levels
69 products = get_products(limit=5)
70 print(f"\nProducts (first 5):")
71 for p in products:
72 print(f" [{p.get('CatalogID')}] {p.get('Name')} β€” Stock: {p.get('Stock')}")

Pro tip: The Shift4Shop API paginates results using limit and offset parameters. For stores with large product catalogs, loop through pages by incrementing offset by limit until the returned array length is less than limit.

Expected result: Running the script prints a list of new orders with amounts and customer names, followed by the first 5 products and their stock levels.

4

Build a Node.js Proxy Server with Express

For web applications, a Node.js Express server acts as a secure proxy between your frontend and the Shift4Shop API, ensuring the API token never reaches the browser. The server exposes clean REST endpoints that your frontend can call without needing to know your Shift4Shop credentials. Install dependencies by running npm install express axios in the Replit Shell. The axios library provides a clean interface for making HTTP requests with default headers, making it easy to maintain the Bearer token across all Shift4Shop API calls. The server binds to 0.0.0.0:3000, which is required for Replit to route external traffic to your app. Configure the .replit file with the correct run command and port mapping. For production use, deploy with Autoscale deployment β€” this gives you a stable HTTPS URL and handles traffic spikes from busy store periods.

server.js
1const express = require('express');
2const axios = require('axios');
3
4const app = express();
5app.use(express.json());
6
7const API_TOKEN = process.env.SHIFT4SHOP_API_TOKEN;
8const STORE_URL = (process.env.SHIFT4SHOP_STORE_URL || '').replace(/\/$/, '');
9const BASE_URL = `${STORE_URL}/api.php`;
10
11const shift4 = axios.create({
12 baseURL: BASE_URL,
13 headers: {
14 Authorization: `Bearer ${API_TOKEN}`,
15 'Content-Type': 'application/json',
16 Accept: 'application/json'
17 }
18});
19
20// GET /orders β€” list recent orders
21app.get('/orders', async (req, res) => {
22 try {
23 const response = await shift4.get('', {
24 params: { Route: 'Orders', limit: req.query.limit || 20, offset: req.query.offset || 0 }
25 });
26 res.json(response.data);
27 } catch (err) {
28 console.error('Shift4Shop error:', err.response?.data);
29 res.status(err.response?.status || 500).json({ error: err.message });
30 }
31});
32
33// GET /orders/:id β€” fetch a single order
34app.get('/orders/:id', async (req, res) => {
35 try {
36 const response = await shift4.get('', {
37 params: { Route: `Orders/${req.params.id}` }
38 });
39 res.json(response.data);
40 } catch (err) {
41 res.status(err.response?.status || 500).json({ error: err.message });
42 }
43});
44
45// GET /products β€” list products
46app.get('/products', async (req, res) => {
47 try {
48 const response = await shift4.get('', {
49 params: { Route: 'Products', limit: req.query.limit || 50, offset: req.query.offset || 0 }
50 });
51 res.json(response.data);
52 } catch (err) {
53 res.status(err.response?.status || 500).json({ error: err.message });
54 }
55});
56
57// PUT /products/:id/stock β€” update product stock
58app.put('/products/:id/stock', async (req, res) => {
59 const { stock } = req.body;
60 if (stock === undefined) {
61 return res.status(400).json({ error: 'stock field required in request body' });
62 }
63 try {
64 const response = await shift4.put('', { Stock: stock }, {
65 params: { Route: `Products/${req.params.id}` }
66 });
67 res.json(response.data);
68 } catch (err) {
69 res.status(err.response?.status || 500).json({ error: err.message });
70 }
71});
72
73// POST /webhook/orders β€” receive Shift4Shop order webhooks
74app.post('/webhook/orders', (req, res) => {
75 const event = req.body;
76 console.log('Shift4Shop webhook:', JSON.stringify(event, null, 2));
77 // Process order event here
78 res.json({ received: true });
79});
80
81app.listen(3000, '0.0.0.0', () => {
82 console.log('Shift4Shop proxy server running on port 3000');
83});

Pro tip: Add a CORS header to allow your frontend to call this proxy: install the cors package (npm install cors) and add app.use(cors()) before your routes. Restrict allowed origins in production to prevent unauthorized access.

Expected result: The server starts on port 3000. A GET request to /orders returns a JSON array of recent store orders.

5

Configure Webhooks and Deploy to Production

Shift4Shop can send webhook notifications to your Replit app when key store events occur β€” new orders, order status changes, and inventory updates. Setting up webhooks eliminates the need to poll the API and lets your integration respond in real time. To configure webhooks in Shift4Shop, go to Settings > Modules and search for 'Webhooks' or 'Real-Time'. Add your deployed Replit app URL plus the webhook path (e.g., https://your-app.replit.app/webhook/orders) as the endpoint URL. Select the events you want to subscribe to. Webhooks require a publicly accessible, always-on URL β€” meaning your Replit app must be deployed, not just running in the development editor. Click the Deploy button in Replit, choose Autoscale deployment for web apps handling store traffic, and wait for the deployment URL to be assigned. Copy the .replit.app URL and paste it into Shift4Shop's webhook configuration. If your store receives high order volumes during promotions or sales, Reserved VM deployment ensures zero cold-start latency β€” important when webhook processing time affects order fulfilment speed. For lower-volume stores, Autoscale deployment is sufficient and more cost-effective.

webhook_server.py
1from flask import Flask, request, jsonify
2import os
3import json
4
5app = Flask(__name__)
6
7@app.route('/webhook/orders', methods=['POST'])
8def order_webhook():
9 """Receive Shift4Shop order event webhooks."""
10 payload = request.json or {}
11
12 # Extract order details from payload
13 order_id = payload.get('OrderID', payload.get('order_id', 'unknown'))
14 status = payload.get('OrderStatus', '')
15 customer_email = payload.get('BillingEmail', '')
16 order_total = payload.get('OrderAmount', 0)
17
18 print(f"Shift4Shop webhook: Order {order_id} | Status: {status} | Customer: {customer_email} | Total: ${order_total}")
19
20 # Handle different event types
21 if status in ('New', '1'):
22 # New order placed β€” trigger fulfilment workflow, send confirmation, etc.
23 print(f"New order {order_id} from {customer_email} for ${order_total}")
24 elif status in ('Shipped', '3'):
25 # Order shipped β€” send tracking notification
26 tracking = payload.get('ShipmentList', [{}])[0].get('TrackingNumber', '')
27 print(f"Order {order_id} shipped, tracking: {tracking}")
28
29 return jsonify({'received': True}), 200
30
31@app.route('/health', methods=['GET'])
32def health():
33 return jsonify({'status': 'ok'})
34
35if __name__ == '__main__':
36 app.run(host='0.0.0.0', port=3000)

Pro tip: Shift4Shop webhook payloads may vary between store configurations and API versions. Always log the raw payload (print(request.json)) during initial setup to inspect the exact field names your store sends before building processing logic.

Expected result: After deploying your Replit app and configuring the webhook URL in Shift4Shop, placing a test order triggers a POST to your server and prints the order details in the deployment logs.

Common use cases

Custom Order Management Dashboard

Build a Replit-hosted web app that pulls live order data from Shift4Shop and displays it in a custom dashboard with filters by status, date range, and customer. Use the Shift4Shop Orders API to fetch pending orders and update their status programmatically without logging into the Shift4Shop admin.

Replit Prompt

Build a Flask web app with a /orders endpoint that fetches all pending orders from the Shift4Shop REST API and returns them as JSON, using SHIFT4SHOP_API_TOKEN and SHIFT4SHOP_STORE_URL from Replit Secrets.

Copy this prompt to try it in Replit

Automated Product Inventory Sync

Sync product inventory levels from a supplier CSV or external warehouse API into Shift4Shop on a schedule. Your Replit backend reads current inventory, compares it to Shift4Shop product stock, and issues PATCH requests to update quantities β€” keeping your storefront inventory accurate without manual intervention.

Replit Prompt

Write a Python script that reads a CSV file of product SKUs and quantities, fetches matching products from the Shift4Shop API by SKU, and updates their stock levels using PATCH requests to the Shift4Shop REST API.

Copy this prompt to try it in Replit

New Order Webhook Processor

Receive Shift4Shop webhook notifications when new orders are placed and trigger downstream workflows β€” such as sending a custom confirmation email, creating a record in your CRM, or dispatching a fulfilment request to a third-party warehouse API. Your Replit app acts as the event hub for all post-order processing.

Replit Prompt

Create an Express server with a POST /webhook/orders endpoint that receives Shift4Shop new-order webhook payloads, logs the order details, and forwards the customer email and order total to a SendGrid email notification.

Copy this prompt to try it in Replit

Troubleshooting

API returns 401 Unauthorized on every request

Cause: The Bearer token in the Authorization header is missing, malformed, or the token has been revoked in Shift4Shop settings.

Solution: Go to your Shift4Shop store manager under Settings > API and verify the token is still active. Regenerate the token if needed and update SHIFT4SHOP_API_TOKEN in Replit Secrets. Make sure the header is formatted as 'Bearer YOUR_TOKEN' with a space between 'Bearer' and the token value.

typescript
1# Python: verify header formatting
2headers = {
3 'Authorization': f'Bearer {os.environ["SHIFT4SHOP_API_TOKEN"]}', # Note the space
4 'Content-Type': 'application/json'
5}

Requests return 200 OK but the response body contains an error message

Cause: The Shift4Shop API sometimes wraps errors in a 200 response body with a Status or Error field rather than using HTTP error codes. This is a known quirk of the Shift4Shop REST API.

Solution: Always check the response body for error indicators after checking the HTTP status code. Look for a 'Status' field with value 'Error' or a non-empty 'Errors' array in the response JSON and raise exceptions accordingly.

typescript
1def check_response(data):
2 """Check Shift4Shop response for embedded errors."""
3 if isinstance(data, dict):
4 if data.get('Status') == 'Error' or data.get('Errors'):
5 raise ValueError(f"Shift4Shop API error: {data.get('Errors') or data.get('Message')}")
6 return data

Webhook events are not being received by the Replit server

Cause: The webhook URL is pointing to the development preview URL instead of the deployed app URL, or the app is not deployed.

Solution: Deploy your Replit app first using the Deploy button. Copy the deployed URL ending in .replit.app and update the webhook URL in Shift4Shop's webhook settings. Development URLs that include 'replit.dev' are temporary and stop working when the editor is closed.

Product list returns empty array even though products exist in the store

Cause: The Route parameter in the API call is case-sensitive or incorrectly formatted, or the API token lacks read permissions for the Products resource.

Solution: Verify the Route parameter uses the exact capitalization shown in the Shift4Shop API documentation (e.g., 'Products' not 'products'). Check that the API token in Settings > API has read access to Products. Test the endpoint directly by appending the route to your store URL in a browser while logged in.

typescript
1# Python: check Route parameter casing
2params = {
3 "Route": "Products", # Capital P β€” case sensitive
4 "limit": 20,
5 "offset": 0
6}

Best practices

  • Store SHIFT4SHOP_API_TOKEN and SHIFT4SHOP_STORE_URL in Replit Secrets (lock icon πŸ”’) β€” never hardcode them in source files or commit them to Git.
  • Build a server-side proxy in Replit so your frontend never directly calls the Shift4Shop API, keeping the store token hidden from browser clients.
  • Always check the response body for Shift4Shop-specific error fields (Status: Error) in addition to the HTTP status code, as the API can return errors wrapped in 200 responses.
  • Paginate product and order queries using limit and offset parameters for stores with large catalogs β€” default limits may truncate results.
  • Deploy with Autoscale for web storefronts and webhook receivers; use Reserved VM if your integration handles high-volume order events during sales periods.
  • Log raw webhook payloads during development to map exact field names before writing processing logic, as payload structure can vary between Shift4Shop plan versions.
  • Use idempotency checks when processing webhooks β€” Shift4Shop may send duplicate events. Store processed order IDs in your database to avoid double-processing.
  • Test API changes against a development store copy before pushing to your live store to avoid accidental inventory or order mutations.

Alternatives

Frequently asked questions

How do I store my Shift4Shop API token in Replit?

Click the lock icon πŸ”’ in the left sidebar of your Replit project to open the Secrets pane. Add SHIFT4SHOP_API_TOKEN with your token value and SHIFT4SHOP_STORE_URL with your store URL. Access them in Python with os.environ['SHIFT4SHOP_API_TOKEN'] and in Node.js with process.env.SHIFT4SHOP_API_TOKEN. Never paste credentials directly into your code.

Does Shift4Shop (3dcart) have a free plan with API access?

Shift4Shop offers a free End-to-End Commerce plan that includes full API access β€” but it requires using Shift4 as your payment processor and is available to US-based stores only. Legacy 3dcart plans require a paid subscription for API access. Check your plan type in your store's Billing section.

How do I find my Shift4Shop store URL for API calls?

Your store URL is the domain you access your store with (e.g., https://www.yourstore.com). All REST API calls are made to STORE_URL/api.php with a Route query parameter. If you use a custom domain, use that as your store URL. You can verify the correct URL in your Shift4Shop admin under Settings > Store.

Can I use Replit to process live orders from Shift4Shop?

Yes. Deploy your Replit app using the Deploy button to get a stable production URL, then configure Shift4Shop webhooks to send order events to that URL. Use Autoscale deployment for web apps and Reserved VM for always-on order processing. Development preview URLs only work while the Replit editor is open.

Why does the Shift4Shop API return errors wrapped in 200 OK responses?

This is a known behavior in the Shift4Shop REST API. Always inspect the response JSON for a Status field equal to 'Error' or an Errors array in addition to checking the HTTP status code. Build a response checker helper function that raises an exception when these error fields are present.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation β€” no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.