# How to Integrate Retool with Shift4Shop (formerly 3dcart)

- Tool: Retool
- Difficulty: Intermediate
- Time required: 20 minutes
- Last updated: April 2026

## TL;DR

Connect Retool to Shift4Shop (formerly 3dcart) using a REST API Resource configured with OAuth token authentication. Set the base URL and access token in the Resources tab, then build visual queries to manage products, orders, and customers. The setup takes about 20 minutes and lets your operations team run bulk actions faster than Shift4Shop's native admin.

## Why Connect Retool to Shift4Shop?

Shift4Shop's built-in admin panel is designed for individual order and product management, but teams running high-volume e-commerce operations frequently outgrow its interface for bulk tasks. Operations teams need to filter orders across multiple criteria, update product stock in bulk, and view customer order histories without clicking through dozens of individual records. Connecting Retool to Shift4Shop's REST API lets you build a faster, more flexible admin panel that fits your team's workflow rather than the platform's default navigation.

Shift4Shop has undergone a significant rebrand from 3dcart to Shift4Shop, and many businesses using the platform are in a transitional state — maintaining legacy configurations while adapting to the new Shift4 payment integration requirements. A Retool panel built over the API provides a consistent operational interface regardless of which era of the platform your store is on, and can surface data from both the e-commerce layer and your internal databases in a single view.

The REST API Resource pattern in Retool is particularly well-suited for Shift4Shop because all requests are proxied server-side, which means your API credentials stay secure and you avoid the CORS issues that would arise from making API calls directly from a browser-based dashboard. This makes it straightforward to build tools that your warehouse staff, customer service agents, and merchandising teams can all use with appropriate Retool permission groups controlling who can read vs. write data.

## Before you start

- A Retool account (Cloud or self-hosted) with permission to add Resources
- A Shift4Shop store with API access enabled (requires a paid plan; free plan does not include API access)
- Your Shift4Shop store's Private API Token (found in Dashboard → API → Private Key in the store control panel)
- Your store's API URL (format: https://apirest.3dcart.com — note that Shift4Shop still uses the 3dcart API domain)
- Basic familiarity with the Retool app builder, query editor, and JavaScript transformer functions

## Step-by-step guide

### 1. Add a Shift4Shop REST API Resource

Go to the Resources tab in Retool — click Resources in the left sidebar from the home page or use the top navigation menu. Click the blue Add Resource button in the upper right. In the resource type selector, search for 'REST' and select REST API to open the configuration form.

Fill in the resource configuration:
- Name: Enter 'Shift4Shop API' or 'Shift4Shop Production' to identify this resource clearly.
- Base URL: Enter the Shift4Shop API base URL. Despite the rebrand, the API endpoint domain is still https://apirest.3dcart.com. The path for the REST API v2 endpoints is /3dCartWebAPI/v2 — you can include this in the base URL so your queries only need to specify the resource path (e.g., /Orders).

For authentication, Shift4Shop uses two header-based credentials for all requests:
1. A store-specific Private Token passed as the SecureURL header
2. A public API key (available from Shift4Shop's developer program) passed as the PrivateKey header

In the Headers section of the resource configuration, add two default headers:
- Key: PrivateKey | Value: {{ retoolContext.configVars.SHIFT4SHOP_PRIVATE_KEY }}
- Key: SecureURL | Value: your store's base URL (e.g., https://yourstore.3dcart.com)
- Key: Content-Type | Value: application/json
- Key: Accept | Value: application/json

Before saving, go to Settings → Configuration Variables in Retool, click Add Variable, and create SHIFT4SHOP_PRIVATE_KEY as a secret variable containing your Private Token. Click Save to create the resource.

**Expected result:** A Shift4Shop REST API resource appears in your Resources list. The private key is stored as a secret configuration variable and referenced securely in the resource headers.

### 2. Query Shift4Shop orders and display in a Table

Create your first data query to retrieve orders from Shift4Shop. In your Retool app, click + New in the query panel at the bottom of the screen. Select your Shift4Shop API resource. Set the HTTP Method to GET and the URL path to /Orders.

In the URL Parameters section, add pagination and filtering parameters that reference components in your app:
- limit: {{ select_pageSize.value || 100 }} — how many orders to return
- offset: {{ (table_orders.pageIndex || 0) * 100 }} — for pagination
- datestart: {{ dateRange.start ? new Date(dateRange.start).toISOString().split('T')[0] : '' }}
- dateend: {{ dateRange.end ? new Date(dateRange.end).toISOString().split('T')[0] : '' }}
- status: {{ statusSelect.value || '' }}

Set the query to Run this query automatically when inputs change and trigger On page load. Add a JavaScript transformer to the query to reshape the response data — Shift4Shop returns an array of order objects directly:

Drag a Table component onto the canvas and set its data source to {{ ordersQuery.data }}. Map columns to: order_id, customer_email, status, total, and order_date. Add a DateRange picker component and a Select component for order status above the Table. The query will re-run automatically when filters change.

To make the order status filter options accurate, set the Select component's values to Shift4Shop's order status codes: New, Pending, Processing, Shipped, Partially Shipped, Cancelled, Hold.

```
// JavaScript transformer to reshape Shift4Shop orders response
const orders = Array.isArray(data) ? data : [];
return orders.map(order => ({
  order_id: order.OrderID,
  invoice_no: order.InvoiceNumber || '',
  customer_name: `${order.BillingFirstName || ''} ${order.BillingLastName || ''}`.trim(),
  customer_email: order.BillingEmail || '',
  status: order.OrderStatus,
  total: parseFloat(order.OrderAmount || 0).toFixed(2),
  order_date: order.OrderDate ? order.OrderDate.split('T')[0] : '',
  ship_date: order.ShipDate ? order.ShipDate.split('T')[0] : 'Not shipped',
  payment_method: order.PaymentMethod || ''
}));
```

**Expected result:** The Table displays a paginated list of Shift4Shop orders with customer names, order totals, statuses, and order dates. The date range and status filters control which orders are shown.

### 3. Query products and build an inventory panel

Create a second query for product and inventory management. Click + New in the query panel and select your Shift4Shop API resource. Set the method to GET and the path to /Products.

Add URL parameters for filtering and pagination:
- limit: 200 (Shift4Shop allows up to 200 per request)
- offset: {{ (productsTable.pageIndex || 0) * 200 }}
- categoryid: {{ categorySelect.value || '' }} — filter by product category
- stockstatus: {{ stockFilter.value || '' }} — filter by in-stock / out-of-stock

Add a JavaScript transformer to flatten the response and calculate a low-stock flag. Drag a Table component into a second tab or section of your app, named 'Inventory'. Set the data source to {{ productsQuery.data }}.

Enable inline editing on the Table for the price and stock_quantity columns. In the Table's Inspector, toggle Allow users to edit this table and set the Save Action to run a PUT query. Create the PUT query:
- Method: PUT
- Path: /Products/{{ productsTable.selectedRow.product_id }}
- Body: { 'Price': {{ productsTable.changesetArray[0]?.price }}, 'Stock': {{ productsTable.changesetArray[0]?.stock_quantity }} }

In the Table's Inspector, set the Save action to trigger this PUT query. Add an On success event handler that refreshes the products query and shows a 'Inventory updated' notification. This lets your merchandising team make stock and price changes directly in the Retool table without touching the Shift4Shop admin interface.

```
// JavaScript transformer for Shift4Shop products
const products = Array.isArray(data) ? data : [];
return products.map(p => ({
  product_id: p.CatalogID,
  sku: p.SKUInfo?.SKU || p.CatalogID,
  name: p.Name,
  price: parseFloat(p.Price || 0).toFixed(2),
  stock_quantity: p.Stock || 0,
  category: p.CategoryID || '',
  status: p.Hide === 'Y' ? 'Hidden' : 'Active',
  low_stock: (p.Stock || 0) < 10
}));
```

**Expected result:** The Inventory tab shows all Shift4Shop products with stock levels, prices, and SKUs. Inline editing is enabled for price and quantity columns, and changes save back to Shift4Shop on click of the Save button.

### 4. Build an order status update workflow

Add the ability to update order statuses directly from your Retool order management panel. This is one of the most common operational tasks for Shift4Shop teams — moving orders from 'Processing' to 'Shipped' or 'Cancelled' in bulk.

First, add a Select component above the orders Table labeled 'Update status to'. Set its values to the Shift4Shop order statuses: Processing, Shipped, Partially Shipped, Cancelled, On Hold. Add a Button labeled 'Apply to Selected Rows'.

Create a JavaScript query that iterates over the Table's selected rows and sends a PUT request for each one:
- In the query editor, select JavaScript as the query type
- Write a loop that triggers a resource query for each selected order row

Because Shift4Shop does not have a bulk update endpoint, you will call the update endpoint individually for each selected order. Use Promise.all() to parallelize the requests for speed. The update endpoint is PUT /Orders/{OrderID} with a body containing the new OrderStatus value.

Create the underlying PUT resource query first: method PUT, path /Orders/{{ currentOrderId }}, body { 'OrderStatus': '{{ newStatus }}' }. Reference this in your JavaScript loop.

In the Apply button's event handler, chain: first run the status update JavaScript query, then on success run the orders query to refresh the table, then show a notification: '{{ selectedRows.length }} orders updated to {{ statusSelect.value }}'.

For compliance tracking, add an On success handler that also inserts a row into your internal PostgreSQL audit table with the Retool user's email, timestamp, affected order IDs, and the new status. This creates an audit trail outside of Shift4Shop.

```
// JavaScript query: bulk update Shift4Shop order statuses
const selectedOrders = ordersTable.selectedRows;
const newStatus = newStatusSelect.value;

if (!selectedOrders || selectedOrders.length === 0) {
  return { error: 'No orders selected' };
}

if (!newStatus) {
  return { error: 'No status selected' };
}

// Trigger update for each selected order in parallel
const updatePromises = selectedOrders.map(order =>
  updateOrderStatusQuery.trigger({
    additionalScope: {
      currentOrderId: order.order_id,
      newStatus: newStatus
    }
  })
);

const results = await Promise.all(updatePromises);
return { updated: results.length, orderIds: selectedOrders.map(o => o.order_id) };
```

**Expected result:** Selecting multiple orders in the Table and clicking 'Apply to Selected Rows' updates all selected orders to the new status in Shift4Shop. The table refreshes automatically, and a notification confirms how many orders were updated.

### 5. Add customer lookup and order history drill-down

Build a customer service view that lets support agents quickly look up a customer's account and see their complete order history from Shift4Shop. This eliminates the need for agents to use the Shift4Shop admin for routine customer inquiries.

Drag a TextInput component onto the canvas labeled 'Search customer by email or name'. Create a query using your Shift4Shop API resource, method GET, path /Customers, URL parameter email: {{ customerSearch.value }} (Shift4Shop supports filtering customers by email). Set this query to Run when inputs change with a 500ms debounce to avoid firing on every keystroke.

Drag a Table component below the search input and bind it to {{ customersQuery.data }}. Display customer ID, name, email, phone, and total lifetime orders. In the Table's event handlers, add an On Row Click handler that triggers a second query to fetch that customer's orders: GET /Orders with the customerid URL parameter set to {{ customersTable.selectedRow.customer_id }}.

Add a second Table below the first (or in a right-side panel using a Container component with a horizontal layout) to display the orders for the selected customer. Bind this to {{ customerOrdersQuery.data }}.

Finally, add a TextArea component below the customer orders table labeled 'Support notes'. Add a Button labeled 'Save Note'. Create a PostgreSQL query (if you have a PostgreSQL resource) that inserts a note with the Retool user's email, timestamp, Shift4Shop customer ID, and the note text. This creates a lightweight CRM layer on top of Shift4Shop data that is stored in your own database and visible to all agents in the Retool app.

```
// JavaScript transformer for customer search results
const customers = Array.isArray(data) ? data : [];
return customers.map(c => ({
  customer_id: c.CustomerID,
  name: `${c.BillingFirstName || ''} ${c.BillingLastName || ''}`.trim(),
  email: c.Email || '',
  phone: c.Phone || '',
  company: c.BillingCompany || '',
  total_orders: c.OrderCount || 0,
  date_joined: c.CustomerSince ? c.CustomerSince.split('T')[0] : ''
}));
```

**Expected result:** Typing in the customer search field triggers the customers query and populates the customer table. Clicking a customer row loads their order history in the adjacent table. Agents can save notes that are stored in the internal database and visible to all team members in the app.

## Best practices

- Store your Shift4Shop Private Token as a secret configuration variable in Retool Settings — never include it directly in query headers or JavaScript code where it could appear in browser network requests.
- Create separate Retool resources for your Shift4Shop sandbox and production environments, and use Retool resource environments to switch between them without modifying app queries.
- Add confirmation modals to all order status update and cancellation actions — these changes affect customer-facing records and should require explicit confirmation with the order details displayed.
- Use JavaScript transformers to normalize Shift4Shop's API response fields before binding to Table components — the API returns inconsistent field names between endpoints and some fields may be null rather than empty strings.
- Implement pagination for all list queries (orders, products, customers) — Shift4Shop limits responses to 200 records per request and returns more data than a Table can display without pagination controls.
- Restrict write access (order status updates, product edits) to specific Retool user groups using Retool's permission groups feature, so warehouse staff can view but not modify order records.
- Log all write operations (status changes, inventory updates) to an internal audit table with the Retool username, timestamp, and changed values for compliance and debugging purposes.
- Use the Shift4Shop API's limit and offset parameters with Retool's Table pagination controls to avoid loading all orders into memory — large stores can have tens of thousands of orders that would cause slow query responses.

## Use cases

### Build a bulk order management panel

Create a Retool dashboard that lists all Shift4Shop orders with filters for date range, order status, payment status, and shipping status. Operations teams can select multiple orders and bulk-update their status, print packing slips, or flag orders for review. A detail panel on the right side shows full order information, customer history, and shipment tracking when an order row is selected.

Prompt example:

```
Build an order management panel that fetches Shift4Shop orders with filters for date range and status (new, processing, shipped, completed, cancelled). Show order ID, customer name, total, payment status, and ship date in a Table. Add a status update dropdown and Apply button for bulk status changes on selected rows.
```

### Create a product inventory management dashboard

Build a Retool panel that shows all Shift4Shop products with current stock levels, SKU, price, and category. Merchandising teams can filter by low stock (quantity below a threshold), search by SKU or product name, and update quantities or prices directly in the table using Retool's inline editing feature. Changes are written back to Shift4Shop via PUT requests triggered by a Save button.

Prompt example:

```
Create a product inventory panel that displays all Shift4Shop products with SKU, name, price, and stock quantity. Add a filter for products with quantity less than 10. Enable inline editing for the price and quantity columns, and add a Save Changes button that sends PUT requests to update each modified product in Shift4Shop.
```

### Build a customer lookup and order history tool

Build a customer service tool that lets support agents search for Shift4Shop customers by email or name, view their profile, and see their complete order history in a side-by-side layout. Agents can see order totals, statuses, and item details without switching between the Shift4Shop admin and other tools. Add a notes field that saves agent comments to your internal PostgreSQL database alongside the Shift4Shop customer ID.

Prompt example:

```
Build a customer lookup tool with a search input for email or customer name that queries the Shift4Shop customers endpoint. Show customer details (name, email, total orders, lifetime value) on the left and their full order history in a Table on the right. Add a text area for agent notes that saves to a PostgreSQL notes table keyed on the Shift4Shop customer ID.
```

## Troubleshooting

### API returns 401 Unauthorized or 'Invalid API key' error on all requests

Cause: The Shift4Shop API requires both a Private Token (store-specific) and a Public API key (from Shift4Shop's developer program) sent as separate headers. If either header is missing or incorrect, all requests will fail with a 401. The header names are case-sensitive: PrivateKey and SecureURL.

Solution: In the Retool Resources tab, click your Shift4Shop resource and verify that both headers are configured: PrivateKey (your store's private token from Dashboard → API → Private Key) and SecureURL (your store's storefront URL). Confirm the values match exactly what you see in the Shift4Shop control panel. If you recently regenerated your API key, update the configuration variable in Retool Settings → Configuration Variables.

### Orders query returns an empty array even though orders exist in the store

Cause: Shift4Shop's /Orders endpoint defaults to returning only 'New' status orders if no status filter is provided in some API versions. Additionally, the date range filters must be in the correct format (YYYY-MM-DD) or they are silently ignored.

Solution: Remove all filters from the query temporarily and run it to confirm the endpoint is working. Then add filters back one at a time. For the date parameters, ensure you are using datestart and dateend (not startdate/enddate) and that the format is YYYY-MM-DD. To return all statuses, omit the status parameter entirely rather than passing an empty string.

```
// Correct URL parameter format for Shift4Shop dates
// In URL Parameters section:
// datestart: {{ dateRange.start ? new Date(dateRange.start).toISOString().split('T')[0] : '' }}
```

### PUT request to update an order fails with 'Order not found' or 404 error

Cause: The order ID format expected in the URL path differs from the OrderID field returned in the GET response. Shift4Shop uses numeric order IDs in the API path, but the response may include both OrderID (numeric) and InvoiceNumber (alphanumeric) fields, and using the wrong one in the PUT path causes a 404.

Solution: Ensure the PUT request path uses the numeric OrderID field from the GET response (e.g., /Orders/12345), not the InvoiceNumber. In your JavaScript transformer, map the numeric field explicitly as order_id: order.OrderID and reference {{ ordersTable.selectedRow.order_id }} in the PUT query path.

### Product stock updates via PUT request return 200 OK but the stock level does not change in Shift4Shop

Cause: Shift4Shop's product update endpoint uses different field names for different product types. Simple products use the Stock field, but products with variants (options) require updating the variant-level stock via a separate /Products/{CatalogID}/OptionSets endpoint, and the top-level PUT ignores the Stock field for these products.

Solution: Check whether the product has variants by examining the OptionSets array in the product GET response. For products with options/variants, query the /Products/{CatalogID}/OptionSets endpoint to get variant IDs, then update each variant's stock individually via PUT /Products/{CatalogID}/OptionSets/{OptionSetID}. In your Retool inventory panel, add a flag column in the transformer that marks products with variants so agents know which ones require the variant-level update flow.

## Frequently asked questions

### Does Shift4Shop have a native Retool connector or must I use a REST API Resource?

Shift4Shop does not have a native Retool connector with pre-built action dropdowns. You connect using a generic REST API Resource in the Resources tab. This requires manually configuring the base URL, authentication headers (PrivateKey and SecureURL), and building queries with the HTTP method, path, and body fields. Despite requiring more initial setup than native connectors, this approach provides full access to all Shift4Shop API endpoints.

### Why does the Shift4Shop API still use 3dcart.com domains after the rebrand?

Shift4Shop maintained backward compatibility with existing integrations when it rebranded from 3dcart. The API base URL (https://apirest.3dcart.com) and some developer portal URLs still use the 3dcart domain. This is intentional and will continue to work for existing and new integrations. Use this URL in your Retool REST API Resource configuration rather than any shift4shop.com domain for API calls.

### Can Retool update Shift4Shop product inventory in bulk?

Yes, but Shift4Shop does not provide a bulk update endpoint — each product must be updated individually via a PUT request to /Products/{CatalogID}. In Retool, you can use a JavaScript query with Promise.all() to send multiple PUT requests in parallel, which is much faster than sequential updates. For very large catalogs (hundreds of products at once), consider batching requests and adding delays to avoid hitting the API rate limit.

### How do I handle Shift4Shop products with variants (options) in Retool?

Products with color, size, or other variants in Shift4Shop have their inventory managed at the option set level, not the top-level product level. When building an inventory management panel, query /Products/{CatalogID}/OptionSets for products that have the OptionSets array populated in the product GET response. Update variant stock via PUT requests to /Products/{CatalogID}/OptionSets/{OptionSetID}. In your Retool transformer, add a has_variants flag to help agents identify which products require the variant update flow.

### Can Retool receive Shift4Shop webhook notifications for new orders?

Yes. Create a Retool Workflow with a Webhook trigger to receive Shift4Shop notifications. The Workflow editor provides a unique HTTPS endpoint URL that you configure in Shift4Shop's control panel under Settings → General → Real-time Notifications or the equivalent webhook settings. When a new order arrives, Shift4Shop POSTs the order data to the Retool Workflow, which can then update your internal database, send a Slack notification, or trigger other downstream actions.

---

Source: https://www.rapidevelopers.com/retool-integrations/3dcart-now-shift4shop
© RapidDev — https://www.rapidevelopers.com/retool-integrations/3dcart-now-shift4shop
