Skip to content
  1. Extras
  2. MiniShop3
  3. Development
  4. REST API

REST API

MiniShop3 provides a REST API for integration with the frontend and external systems.

Entry points

PurposeURLAuthorization
Web API (frontend)/assets/components/minishop3/api.phpMS3TOKEN token
Manager API (manager)/assets/components/minishop3/connector.phpMODX session

This documentation describes the Web API for the frontend.

Base URL

/assets/components/minishop3/api.php?route=/api/v1/{endpoint}

All requests pass the route via the route parameter.

Authorization

Getting a token

Before working with the cart and orders you need to get a client token:

http
GET /api/v1/customer/token/get

Response:

json
{
  "success": true,
  "data": {
    "token": "abc123def456..."
  },
  "message": ""
}

As of v1.6, the token is stored in an httpOnly cookie ms3_token. The server sets the cookie when the token is obtained or refreshed.

Security

The httpOnly cookie is not accessible from JavaScript, which protects the token from XSS. The browser sends the cookie with every request.

Token resolution order on the server (TokenMiddleware):

  1. Authorization: Bearer {token} header (for mobile apps)
  2. HTTP_MS3TOKEN header (legacy)
  3. httpOnly cookie ms3_token (primary for web)

The cookie is configured using MODX session parameters: session_cookie_domain, session_cookie_path, session_cookie_secure, session_cookie_samesite.

Response format

All responses use a single format:

Success:

json
{
  "success": true,
  "data": { ... },
  "message": "Success message"
}

Error:

json
{
  "success": false,
  "message": "Error description",
  "code": 400
}

Cart

Add product

http
POST /api/v1/cart/add

Parameters:

ParameterTypeRequiredDescription
idintYesProduct ID
countintNoQuantity (default 1)
optionsobjectNoProduct options (color, size, etc.)
renderarrayNoSnippet tokens for SSR

Request example:

javascript
fetch('/assets/components/minishop3/api.php?route=/api/v1/cart/add&ms3_token=' + token, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        id: 123,
        count: 2,
        options: {
            color: 'Red',
            size: 'XL'
        }
    })
})

Response:

json
{
  "success": true,
  "data": {
    "last_key": "123_a1b2c3d4",
    "cart": [
      {
        "key": "123_a1b2c3d4",
        "id": 123,
        "count": 2,
        "price": 1500,
        "cost": 3000,
        "weight": 0.5,
        "options": {"color": "Red", "size": "XL"},
        "name": "Product",
        "thumb": "/assets/images/product.jpg"
      }
    ],
    "status": {
      "total_count": 2,
      "total_cost": 3000,
      "total_weight": 1.0,
      "total_positions": 1
    }
  },
  "message": "Product added to cart"
}

Change quantity

http
POST /api/v1/cart/change

Parameters:

ParameterTypeRequiredDescription
product_keystringYesUnique product key in cart
countintYesNew quantity

Example:

json
{
  "product_key": "123_a1b2c3d4",
  "count": 5
}

Remove product

http
POST /api/v1/cart/remove

Parameters:

ParameterTypeRequiredDescription
product_keystringYesUnique product key

Get cart

http
GET /api/v1/cart/get

Response:

json
{
  "success": true,
  "data": {
    "cart": [...],
    "status": {
      "total_count": 5,
      "total_cost": 7500,
      "total_weight": 2.5,
      "total_positions": 3
    }
  }
}

Clear cart

http
POST /api/v1/cart/clean

Order

Get order draft

http
GET /api/v1/order/get

Response:

json
{
  "success": true,
  "data": {
    "order": {
      "email": "user@example.com",
      "phone": "+7 999 123-45-67",
      "first_name": "John",
      "delivery": 1,
      "payment": 1,
      "address_city": "Moscow",
      "address_street": "Main St",
      "comment": ""
    },
    "deliveries": [...],
    "payments": [...]
  }
}

Add/update field

http
POST /api/v1/order/add

Parameters:

ParameterTypeRequiredDescription
keystringYesField name
valuemixedYesValue

Available fields:

FieldDescription
emailCustomer email
phonePhone
first_nameFirst name
last_nameLast name
deliveryDelivery method ID
paymentPayment method ID
commentOrder comment
cityCity
streetStreet
buildingBuilding
roomApartment/office
indexPostal code
address_hashSaved address hash

Example:

json
{
  "key": "email",
  "value": "user@example.com"
}

Set multiple fields

http
POST /api/v1/order/set

Parameters:

ParameterTypeRequiredDescription
fieldsobjectYesObject with fields

Example:

json
{
  "fields": {
    "email": "user@example.com",
    "phone": "+79991234567",
    "first_name": "John",
    "delivery": 1
  }
}

Remove field

http
POST /api/v1/order/remove

Parameters:

ParameterTypeRequiredDescription
keystringYesField name

Submit order

http
POST /api/v1/order/submit

Response (success):

json
{
  "success": true,
  "data": {
    "order_id": 15,
    "order_num": "24/12-15",
    "redirect_url": "/thank-you?msorder=15"
  },
  "message": "Order submitted successfully"
}

Response (validation error):

json
{
  "success": false,
  "message": "Fill required fields",
  "data": {
    "errors": {
      "email": "Enter email",
      "phone": "Enter phone"
    }
  },
  "code": 400
}

Clear order

http
POST /api/v1/order/clean

Cost

Total cost

http
GET /api/v1/order/cost

Response:

json
{
  "success": true,
  "data": {
    "cart_cost": 5000,
    "delivery_cost": 300,
    "payment_cost": 0,
    "total_cost": 5300,
    "discount": 0
  }
}

Cart cost

http
GET /api/v1/order/cost/cart

Delivery cost

http
GET /api/v1/order/cost/delivery

Payment fee

http
GET /api/v1/order/cost/payment

Delivery addresses

Set saved address

http
POST /api/v1/order/address/set

Parameters:

ParameterTypeRequiredDescription
address_hashstringYesAddress MD5 hash

Clear address

http
POST /api/v1/order/address/clean

Delivery validation

Validation rules

http
GET /api/v1/order/delivery/validation-rules

Response:

json
{
  "success": true,
  "data": {
    "city": {"required": true, "min": 2},
    "street": {"required": true},
    "building": {"required": true},
    "phone": {"required": true, "pattern": "^\\+?[0-9]+$"}
  }
}

Required fields

http
GET /api/v1/order/delivery/required-fields

Response:

json
{
  "success": true,
  "data": ["city", "street", "building", "phone"]
}

Customer

Registration

http
POST /api/v1/customer/register

Parameters:

ParameterTypeRequiredDescription
emailstringYesEmail
passwordstringYesPassword
password_confirmstringYesPassword confirmation
first_namestringNoFirst name
last_namestringNoLast name
phonestringNoPhone
privacy_acceptedboolDepends on settingsData processing consent

Response:

json
{
  "success": true,
  "object": {
    "customer": {
      "id": 5,
      "email": "user@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "phone": "+79991234567",
      "email_verified": false
    },
    "token": "abc123def456...",
    "expires_at": "2026-03-16 12:34:56",
    "email_verification_required": false,
    "redirect_url": ""
  },
  "message": "Registration successful"
}

Breaking change (v1.6)

Registration response format changed:

  • Before (v1.5): token — object {token: "...", expires_at: "..."}
  • After (v1.6): token — string, expires_at at top level

Custom themes that use result.object.token.token should switch to result.object.token.

Login

http
POST /api/v1/customer/login

Parameters:

ParameterTypeRequiredDescription
emailstringYesEmail
passwordstringYesPassword

Response:

json
{
  "success": true,
  "data": {
    "customer_id": 5,
    "token": "session_token_xyz789",
    "customer": {
      "id": 5,
      "email": "user@example.com",
      "first_name": "John"
    }
  }
}

Update profile

http
PUT /api/v1/customer/profile

Requires authorization (authenticated customer token)

Parameters:

ParameterTypeDescription
first_namestringFirst name
last_namestringLast name
phonestringPhone

Email verification

http
GET /api/v1/customer/email/verify?token=verification_token

Resend verification

http
POST /api/v1/customer/email/resend-verification

Requires authorization

Customer addresses

All endpoints require authorization.

List addresses

http
GET /api/v1/customer/addresses

Response:

json
{
  "success": true,
  "data": [
    {
      "id": 1,
      "hash": "abc123...",
      "city": "Moscow",
      "street": "Main St",
      "building": "10",
      "room": "5",
      "is_default": true
    }
  ]
}

Get address

http
GET /api/v1/customer/addresses/{id}

Create address

http
POST /api/v1/customer/addresses

Parameters:

ParameterTypeDescription
citystringCity
streetstringStreet
buildingstringBuilding
roomstringApartment/office
indexstringPostal code
countrystringCountry
regionstringRegion
is_defaultboolDefault address

Update address

http
PUT /api/v1/customer/addresses/{id}

Delete address

http
DELETE /api/v1/customer/addresses/{id}

Set default address

http
PUT /api/v1/customer/addresses/{id}/set-default

Customer orders

Cancel order

http
POST /api/v1/customer/orders/{id}/cancel

Requires authorization (authenticated customer token)

Cancels the customer's order if the current status is in the allowed list (ms3_customer_cancel_allowed_statuses).

Response (success):

json
{
  "success": true,
  "message": "Order canceled",
  "data": {
    "order_id": 15,
    "status_id": 5
  }
}

Response (error — status not allowed):

json
{
  "success": false,
  "message": "Order cannot be canceled in its current status",
  "code": 400
}

Response (error — not found):

json
{
  "success": false,
  "message": "Order not found",
  "code": 404
}

Related settings:

SettingDescription
ms3_customer_cancel_allowed_statusesStatus IDs for which cancellation is allowed (default 2,3)
ms3_status_canceledTarget status ID for canceled orders

Health Check

http
GET /api/v1/health

Response:

json
{
  "success": true,
  "data": {
    "status": "ok",
    "version": "1.0.0",
    "timestamp": 1703952000,
    "api": "web"
  }
}

Middleware

CORS

Configured via system setting ms3_cors_allowed_origins:

  • * — allow all domains
  • https://example.com,https://shop.example.com — list of domains

Rate Limiting

DDoS protection via system settings:

  • ms3_rate_limit_max_attempts — max requests (default 60)
  • ms3_rate_limit_decay_seconds — period in seconds (default 60)

When limit is exceeded:

json
{
  "success": false,
  "message": "Too many requests",
  "code": 429
}

SSR (Server-Side Rendering)

The API supports server-side HTML rendering for updating parts of the page.

Usage

Pass an array of snippet tokens in the render parameter:

javascript
fetch('/api/v1/cart/add?ms3_token=' + token, {
    method: 'POST',
    body: JSON.stringify({
        id: 123,
        render: ['ms3_abc123...', 'ms3_def456...']
    })
})

Response includes HTML:

json
{
  "success": true,
  "data": {
    "cart": [...],
    "status": {...},
    "render": {
      "ms3_abc123...": "<div class=\"cart\">...</div>",
      "ms3_def456...": "<span class=\"count\">5</span>"
    }
  }
}

Registering snippets

Tokens are generated automatically when calling snippets with the selector parameter:

fenom
{'msCart' | snippet: [
    'tpl' => 'tpl.msCart',
    'selector' => '#cart-container'
]}

Custom routes

To add your own endpoints create a file:

core/config/ms3_routes_web.custom.php

Example:

php
<?php
use MiniShop3\Router\Response;

$router->group('/api/v1', function($router) use ($modx) {

    $router->get('/custom/endpoint', function($params) use ($modx) {
        return Response::success(['custom' => 'data']);
    });

});

Custom routes are loaded after system routes and can override them.

JavaScript client

MiniShop3 provides a JavaScript library for working with the API:

javascript
// Add to cart
ms3.cart.add(123, 2, {color: 'red'});

// Submit order
ms3.order.submit();

// Handle responses via hooks
ms3.hooks.add('afterAddToCart', ({response}) => {
    console.log('Product added', response.data);
});

See Frontend JavaScript for details.

Error codes

CodeDescription
400Bad request (missing parameters, validation error)
401Authorization token required
403Access denied
404Resource not found
429Too many requests
500Internal server error

Debugging

Enable debug mode via the ms3_api_debug setting:

json
{
  "success": false,
  "message": "Internal server error",
  "code": 500,
  "debug": {
    "exception": "Exception",
    "message": "Detailed error message",
    "file": "/path/to/file.php",
    "line": 123
  }
}

Security

Do not enable debug mode in production — it exposes the application's internal structure.