# How to Integrate Bolt.new with PrestaShop

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: April 2026

## TL;DR

Connect Bolt.new to PrestaShop by enabling the Webservice API in your store's admin panel, generating an API key with the permissions you need, and calling endpoints through a Next.js API route. PrestaShop's Webservice API returns XML by default — always append output_format=JSON to requests. Store your API key in .env server-side; never expose it in client code. Webhooks require a deployed Netlify or Bolt Cloud URL.

## Build a Custom PrestaShop Storefront or Dashboard in Bolt.new

PrestaShop is the dominant open-source e-commerce platform in France, Spain, and Latin America, with over 300,000 live stores. Its Webservice API exposes products, categories, orders, customers, cart rules, and inventory over HTTP — making it fully accessible from Bolt.new through server-side API routes. Whether you're building a custom storefront that replaces PrestaShop's default theme, an internal order management dashboard, or a business analytics view, Bolt can generate the React frontend while PrestaShop handles all commerce logic.

The most important thing to understand about PrestaShop's Webservice API before writing any code: it returns XML by default. Every request must include the query parameter output_format=JSON, or you'll receive an XML response that your JavaScript cannot easily parse. This is a common source of confusion for developers new to PrestaShop. The good news is that once you add this parameter consistently, the JSON responses are well-structured and straightforward to work with. Your Next.js API route helper should add this parameter automatically so you never forget.

PrestaShop's API uses a key-based authentication model — there is no OAuth or JWT flow. You generate an API key in the admin panel, assign it specific resource permissions (GET, POST, PUT, DELETE for each resource type), and authenticate using HTTP Basic auth with the API key as the username and an empty password. The API key maps 1-to-1 with permissions, so create separate keys for different applications with minimum required scopes. Compared to Magento, PrestaShop's API is simpler and less verbose; compared to BigCommerce, it requires more manual setup since PrestaShop is self-hosted. For stores already running PrestaShop, this integration provides a modern React frontend without migrating the commerce backend.

## Before you start

- A PrestaShop 1.7.x or 8.x store with admin access (self-hosted or on a managed PrestaShop host)
- Webservice API enabled in your store (Advanced Parameters → Webservice → Enable PrestaShop's webservice = Yes, and Enable CGI mode if required by your host)
- A Webservice key created with the resource permissions your app needs (GET on Products, Categories, Orders, Customers as a minimum)
- Your store's base URL (e.g., https://yourstore.com) — not the admin URL
- A Bolt.new project using Next.js (request Next.js explicitly when creating the project for API route support)

## Step-by-step guide

### 1. Enable the PrestaShop Webservice and Create an API Key

Log in to your PrestaShop admin panel. Navigate to Advanced Parameters → Webservice. Set 'Enable PrestaShop's webservice' to Yes. Some hosting configurations also require 'Enable CGI mode for Apache' to be set to Yes — check your host's documentation if API calls fail with authentication errors despite correct credentials. Click Save. Now click 'Add new webservice key' (the + button or Add Key). You'll see a form with a Key field, Description, Status, and resource permissions. Click 'Generate' next to the Key field to auto-generate a secure 32-character API key — copy and save it immediately, as you'll need to enter it manually if you lose it. Give it a description like 'Bolt Frontend App'. Under permissions, select the resources you need: for a storefront, enable GET (read) on products, categories, product_features, product_option_values, images, languages, currencies, and countries. For an order dashboard, also enable GET and PUT on orders and order_states. Setting overly broad permissions (all resources, all verbs) is a security risk — stick to minimum required permissions. Set Status to Enabled and click Save. In Bolt.new, add to your .env file: PRESTASHOP_BASE_URL=https://yourstore.com and PRESTASHOP_API_KEY=your_32_char_key. Do not use VITE_ or NEXT_PUBLIC_ prefix — this key must stay server-side only.

```
// lib/prestashop.ts
const BASE_URL = process.env.PRESTASHOP_BASE_URL;
const API_KEY = process.env.PRESTASHOP_API_KEY;

// Basic auth: key as username, empty string as password
const authHeader = `Basic ${Buffer.from(`${API_KEY}:`).toString('base64')}`;

interface PrestaShopRequestOptions {
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
  body?: unknown;
  params?: Record<string, string>;
}

export async function prestashopApi<T>(
  path: string,
  options: PrestaShopRequestOptions = {}
): Promise<T> {
  const url = new URL(`${BASE_URL}/api${path}`);
  // CRITICAL: Always add output_format=JSON or PrestaShop returns XML
  url.searchParams.set('output_format', 'JSON');
  if (options.params) {
    Object.entries(options.params).forEach(([k, v]) => url.searchParams.set(k, v));
  }

  const response = await fetch(url.toString(), {
    method: options.method ?? 'GET',
    headers: {
      Authorization: authHeader,
      'Content-Type': 'application/json',
      Accept: 'application/json',
    },
    body: options.body ? JSON.stringify(options.body) : undefined,
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`PrestaShop API ${response.status}: ${errorText}`);
  }

  return response.json() as Promise<T>;
}
```

**Expected result:** The prestashopApi helper is configured. Calling it from any Next.js API route proxies requests to your PrestaShop store with Basic auth and forces JSON output.

### 2. Fetch Products and Build the Product Catalog

PrestaShop's products endpoint supports several display modes that affect response structure. The default display=full returns all fields including multilingual name arrays, descriptions, combinations (variants), and nested associations. Product names in PrestaShop are multilingual objects — an array of { id: langId, value: 'Product Name' } pairs. You need to extract the name for your store's active language ID (usually 1 for English, but varies by store configuration). Product images in PrestaShop are retrieved through a separate endpoint or constructed from a URL pattern: {PRESTASHOP_BASE_URL}/api/images/products/{productId}/{imageId}. The product object includes an associations.images array with image IDs — use the first image ID as the main product image. Product prices in PrestaShop are pre-tax by default (price field) — the price_with_reduction field shows the discounted price if a cart rule applies. For display purposes, fetch the price including tax: multiply price by (1 + tax_rate/100) or use PrestaShop's /api/products/{id}?price[use_tax]=1 parameter. A simpler approach: use the priceDisplay property from the product's product_prices endpoint. For the initial catalog page, fetching price and price_reduction_without_tax gives you enough to show regular and sale prices.

```
// app/api/products/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { prestashopApi } from '@/lib/prestashop';

interface PSProductName { id: string; value: string; }
interface PSProductImage { id: string; }

interface PSProduct {
  id: number;
  name: PSProductName[] | string;
  description_short: PSProductName[] | string;
  price: string;
  associations?: { images?: { id: string }[] };
}

interface PSProductsResponse {
  products: PSProduct[];
}

function extractLangValue(field: PSProductName[] | string, langId = '1'): string {
  if (typeof field === 'string') return field;
  return field.find((f) => f.id === langId)?.value ?? field[0]?.value ?? '';
}

export async function GET(request: NextRequest) {
  const page = parseInt(request.nextUrl.searchParams.get('page') ?? '1', 10);
  const limit = 24;
  const offset = (page - 1) * limit;
  const baseUrl = process.env.PRESTASHOP_BASE_URL;

  try {
    const data = await prestashopApi<PSProductsResponse>('/products', {
      params: {
        display: 'full',
        limit: String(limit),
        offset: String(offset),
        'filter[active]': '1',
        sort: '[date_add_DESC]',
      },
    });

    const products = (data.products ?? []).map((p) => {
      const firstImageId = p.associations?.images?.[0]?.id;
      return {
        id: p.id,
        name: extractLangValue(p.name),
        description: extractLangValue(p.description_short),
        price: parseFloat(p.price),
        imageUrl: firstImageId
          ? `${baseUrl}/api/images/products/${p.id}/${firstImageId}`
          : null,
      };
    });

    return NextResponse.json({ products });
  } catch (error) {
    return NextResponse.json({ error: 'Failed to fetch products' }, { status: 500 });
  }
}
```

**Expected result:** Visiting /api/products returns a JSON array of products with names extracted in the correct language, prices, and image URLs. The React catalog page renders product cards.

### 3. Fetch Orders and Build the Management Dashboard

PrestaShop orders can be fetched from the /api/orders endpoint with display=full to get all fields including customer, address, and line item data. Order status IDs in PrestaShop refer to entries in the order_states resource — status ID 2 means 'Payment Accepted', 3 is 'In Progress', 4 is 'Shipped', etc., but the exact IDs depend on your store's configuration. For a management dashboard, you should fetch order states separately from /api/order_states and build a map of ID to label. Customer names are not directly on the order object — the order has an id_customer field; you need a separate call to /api/customers/{id} to get the name. For efficiency, deduplicate customer IDs across fetched orders and make one API call per unique customer. Alternatively, use PrestaShop's association display to include nested customer data in the orders response: add associations=customer to your request. Order line items (cart products) are in associations.order_rows or available via /api/order_details?filter[id_order]=X. Order financial data includes total_paid_tax_incl (what the customer paid), total_products_wt (product cost with tax), and total_shipping_tax_incl (shipping cost). All are string-formatted decimal numbers — parse with parseFloat before arithmetic.

```
// app/api/orders/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { prestashopApi } from '@/lib/prestashop';

interface PSOrder {
  id: number;
  reference: string;
  id_customer: string;
  total_paid_tax_incl: string;
  current_state: string;
  date_add: string;
  valid: string;
}

interface PSOrdersResponse {
  orders: PSOrder[];
}

export async function GET(request: NextRequest) {
  const statusFilter = request.nextUrl.searchParams.get('state');

  const params: Record<string, string> = {
    display: 'full',
    limit: '30',
    sort: '[date_add_DESC]',
  };

  if (statusFilter) {
    params['filter[current_state]'] = statusFilter;
  }

  try {
    const data = await prestashopApi<PSOrdersResponse>('/orders', { params });

    const orders = (data.orders ?? []).map((o) => ({
      id: o.id,
      reference: o.reference,
      customerId: o.id_customer,
      total: parseFloat(o.total_paid_tax_incl),
      statusId: parseInt(o.current_state, 10),
      date: o.date_add,
    }));

    return NextResponse.json({ orders });
  } catch (error) {
    return NextResponse.json({ error: 'Failed to fetch orders' }, { status: 500 });
  }
}
```

**Expected result:** The orders API route returns recent orders with status IDs and totals. The dashboard shows orders in a table with status labels from the dynamically fetched order states map.

### 4. Set Up Webhook Notifications After Deployment

PrestaShop does not include a native webhook system in its core — unlike Stripe or BigCommerce, there is no 'register a webhook URL' button in the admin panel. Webhook functionality in PrestaShop requires either a third-party module from the PrestaShop Marketplace (search for 'webhooks' or 'event notifications') or a custom PrestaShop module that hooks into Magento's event system (called 'Hooks' in PrestaShop). For teams comfortable with PHP, writing a simple custom module that calls your deployed URL on actionOrderStatusUpdate and actionOrderAdd hooks is straightforward. For teams who prefer not to write PHP, the PrestaShop Marketplace has paid modules like 'Webhook PRO' and 'HTTP Request Hooks' that configure event-to-URL mappings through the admin panel. Alternatively, build a polling approach: a scheduled fetch from your app's /api/orders/recent route every 1-5 minutes to check for new orders. This avoids webhook configuration entirely and works well for dashboards that don't require millisecond-level real-time data. The same WebContainer constraint applies: your Bolt preview has no public URL, so configure webhooks against your deployed Netlify or Bolt Cloud domain. Test the webhook by manually triggering an order status change in PrestaShop admin and checking your server logs for the incoming POST.

```
// app/api/webhooks/prestashop/route.ts
// Works only after deployment to Netlify or Bolt Cloud
// (Bolt's WebContainer has no public URL for incoming traffic)
import { NextRequest, NextResponse } from 'next/server';

interface PrestaShopWebhookPayload {
  event: string;
  id_order?: number;
  id_order_state?: number;
  reference?: string;
  total?: number;
  [key: string]: unknown;
}

export async function POST(request: NextRequest) {
  const payload: PrestaShopWebhookPayload = await request.json();

  switch (payload.event) {
    case 'order_add':
      console.log(`New PrestaShop order #${payload.reference} (ID: ${payload.id_order}), total: ${payload.total}`);
      // TODO: send confirmation email, update inventory, notify fulfillment
      break;

    case 'order_status_update':
      console.log(`Order #${payload.id_order} status updated to state ${payload.id_order_state}`);
      // TODO: notify customer, update tracking
      break;

    default:
      console.log(`Unhandled PrestaShop webhook event: ${payload.event}`, payload);
  }

  // Always return 200 quickly — webhook modules retry on non-200 responses
  return NextResponse.json({ received: true });
}
```

**Expected result:** After deploying and installing a PrestaShop webhook module configured with your deployed URL, placing an order in PrestaShop triggers a POST to /api/webhooks/prestashop with order data visible in server logs.

## Best practices

- Always proxy PrestaShop Webservice calls through a Next.js API route — the API key must never appear in client-side code, browser network tabs, or React component fetch calls
- Build the output_format=JSON parameter into your API helper so it is impossible to accidentally omit it — a request returning XML will fail silently in ways that are hard to debug
- Use minimum required permissions when creating Webservice keys — a storefront that only reads products doesn't need write access to orders, and limiting scopes reduces damage if a key is compromised
- Cache API responses where data changes infrequently — product catalogs, categories, and currencies rarely change and can be cached for several minutes, reducing API calls and improving storefront performance
- Extract multilingual field values using your store's active language ID, not by assuming index 0 is always the correct language
- Handle the case where associations.images is empty or missing — not all PrestaShop products have images, and accessing [0].id on an empty array will throw
- Deploy to Netlify or Bolt Cloud early to test your full integration in a real environment — some PrestaShop API behaviors differ between localhost and production hosting configurations

## Use cases

### Custom Product Catalog with Category Filtering

Replace PrestaShop's default theme with a high-performance React storefront that fetches product listings and categories from the Webservice API. Display products in a filterable grid with real-time search, category navigation, and price range filters. The custom React frontend is faster than PrestaShop's default Smarty-based themes on most hosting configurations.

Prompt example:

```
Build a custom PrestaShop product catalog. Create a Next.js API route at app/api/products/route.ts that fetches products from my PrestaShop store using the Webservice API. Call /api/products?output_format=JSON&display=full&limit=24 authenticated with Basic auth using PRESTASHOP_API_KEY. Return product id, name, description_short, price, and the main product image URL constructed from PRESTASHOP_BASE_URL. Build a React ProductGrid component that shows products in a 3-column grid with image, name, price, and category filter buttons at the top.
```

### Order Dashboard for Store Operations

Build an operations dashboard that shows recent PrestaShop orders with status filtering, order details, and the ability to update order statuses without logging into the full admin panel. Useful for fulfillment teams who need a focused view without access to product management or store settings.

Prompt example:

```
Create an order management dashboard for my PrestaShop store. Build a Next.js API route that fetches orders from /api/orders?output_format=JSON&display=full&limit=50&sort=[date_add_DESC]. Show orders in a table with order reference, customer name (from /api/customers/{id}), total price with tax, current status (from /api/order_states/{id}), and date. Add a filter for order status. Allow marking an order as shipped by sending a PUT request to /api/orders/{id} with the updated current_state. Use PRESTASHOP_BASE_URL and PRESTASHOP_API_KEY from environment variables.
```

### Inventory and Stock Monitoring

Build a dashboard showing PrestaShop stock levels across all products and variants. Highlight out-of-stock and low-stock items, show movement history, and allow quick stock updates. PrestaShop's stock management API (stock_availables resource) tracks quantity per product and product attribute combination.

Prompt example:

```
Build an inventory monitoring dashboard for my PrestaShop store. Fetch stock data from /api/stock_availables?output_format=JSON&display=full. Each stock_available item has id_product, id_product_attribute (variant), and quantity fields. Join with product names from /api/products. Display a dashboard with: total SKUs tracked, out-of-stock count, and a table showing product name, variant (if any), current quantity, and a color badge (out-of-stock = red if qty 0, low-stock = yellow if qty under 5, in-stock = green). Allow editing quantity with a PUT request to /api/stock_availables/{id}.
```

## Troubleshooting

### API responses return XML strings instead of JSON, causing JSON.parse errors

Cause: PrestaShop's Webservice API returns XML by default. The output_format=JSON query parameter is missing from the request, so PrestaShop sends XML which JavaScript cannot automatically parse.

Solution: Always include output_format=JSON in every API request. The lib/prestashop.ts helper in this guide adds it automatically — make sure all API calls use this helper rather than calling fetch directly.

```
// Without the helper, manually add output_format=JSON
const url = new URL(`${BASE_URL}/api/products`);
url.searchParams.set('output_format', 'JSON'); // REQUIRED — PrestaShop returns XML without this
const response = await fetch(url.toString(), { headers: { Authorization: authHeader } });
```

### API returns 401 Unauthorized even though the API key looks correct

Cause: HTTP Basic auth for PrestaShop Webservice uses the API key as the username with an empty password. The format is base64(key + ':') — note the colon with nothing after it. Using key alone (without the trailing colon) fails authentication.

Solution: Verify your Authorization header is Base64-encoded with a trailing colon: Buffer.from('YOUR_API_KEY:').toString('base64'). Also check that the Webservice is enabled in Advanced Parameters → Webservice (the 'Enable PrestaShop's webservice' toggle must be Yes) and that your key's Status is Enabled.

```
// Correct Basic auth format for PrestaShop — key as username, empty password
const authHeader = `Basic ${Buffer.from(`${apiKey}:`).toString('base64')}`;
// NOT: `Basic ${Buffer.from(apiKey).toString('base64')}`  ← missing colon = 401
```

### Product names return as arrays of objects instead of a string

Cause: PrestaShop stores multilingual fields (name, description, description_short) as arrays of language-value pairs: [{ id: '1', value: 'Product Name' }, { id: '2', value: 'Nom du Produit' }]. The API returns all translations simultaneously.

Solution: Extract the value for your store's language ID using the extractLangValue pattern shown in the products route above. Language ID 1 is typically English or your first installed language. Check your store's language IDs in Internationalization → Languages in the admin panel.

```
// Extract a single language value from PrestaShop's multilingual array
function extractLangValue(
  field: Array<{ id: string; value: string }> | string,
  langId = '1'
): string {
  if (typeof field === 'string') return field;
  return field.find((f) => f.id === langId)?.value ?? field[0]?.value ?? '';
}

// Usage
const productName = extractLangValue(product.name, '1');
```

### Webhook events never arrive at the app's endpoint during development

Cause: Bolt's WebContainer runs in the browser and has no publicly accessible URL. PrestaShop's webhook module (or custom module) sends HTTP POST requests to a URL, but the WebContainer cannot receive incoming traffic — there is no public IP or domain for the preview environment.

Solution: Deploy to Netlify or Bolt Cloud to obtain a public HTTPS URL. Configure your PrestaShop webhook module with the deployed domain (e.g., https://your-app.netlify.app/api/webhooks/prestashop). Test by placing an order or changing an order status in PrestaShop admin and checking your deployed app's logs.

## Frequently asked questions

### How do I connect Bolt.new to PrestaShop?

Enable PrestaShop's Webservice API in Advanced Parameters → Webservice, create an API key with the resource permissions your app needs, and copy the key into your Bolt project's .env file as PRESTASHOP_API_KEY. Build a Next.js API route that proxies requests to PrestaShop using Basic authentication (key as username, empty password). Always append output_format=JSON to every request — PrestaShop returns XML by default.

### Why does PrestaShop return XML instead of JSON?

PrestaShop's Webservice API was designed before JSON became the universal standard and defaults to XML responses. You must append output_format=JSON as a query parameter to every API call to receive JSON. Build this into your API helper function so it's applied automatically to all requests.

### Can I use PrestaShop with Bolt.new during development in the preview?

Yes — outbound API calls from your Next.js API routes work fine in Bolt's WebContainer during development. Your server-side routes can call PrestaShop's API and return data to React components. The WebContainer limitation applies to incoming traffic: PrestaShop webhooks cannot reach your preview environment. Deploy to Netlify or Bolt Cloud before testing webhook flows.

### Does PrestaShop have a native webhook system like Stripe or BigCommerce?

Not in the core platform. PrestaShop's core does not include webhook configuration. To receive webhook events, you need to install a third-party webhook module from the PrestaShop Marketplace (several options exist, both free and paid) or build a custom PrestaShop module in PHP that hooks into PrestaShop's event system. Alternatively, implement a polling approach from your Bolt app that periodically checks for new orders.

### How do I handle PrestaShop's multilingual product names in Bolt?

PrestaShop stores text fields like name and description as arrays of language-value pairs — one entry per installed language. Extract the name for your store's active language by filtering the array by language ID: field.find(f => f.id === '1')?.value. Language ID '1' is typically your first installed language. Check your store's language IDs in Internationalization → Languages in the admin panel.

### How do I deploy a Bolt.new PrestaShop app to Netlify?

In Bolt, go to Settings → Applications and connect Netlify via OAuth, then click Publish. After deploying, add PRESTASHOP_BASE_URL and PRESTASHOP_API_KEY to Netlify's Site Configuration → Environment Variables and trigger a redeploy. If you've set up PrestaShop webhooks, update the webhook module with your new Netlify domain.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/prestashop
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/prestashop
