Studio Integration
Studio API Endpoints
The Ecommerce Framework Bundle provides a RESTful API for the Pimcore Studio interface, enabling management of
pricing rules, access to product index data, and back-office order management. All endpoints are prefixed with
/pimcore-studio/api/bundle/ecommerce and require appropriate permissions.
Index
List Tenants
Endpoint: GET /tenants
Operation ID: bundle_ecommerce_tenants_collection
Permission: bundle_ecommerce_back-office_order
Returns a list of all configured ecommerce tenants.
Response: Paginated collection of Tenant objects
List Index Fields
Endpoint: GET /fields
Operation ID: bundle_ecommerce_fields_collection
Permission: bundle_ecommerce_back-office_order
Returns a list of product index fields, with optional filtering by filter group, tenant, and visibility.
Query Parameters:
filtergroup(string, optional): Filter by specific filter groups, comma-separated (e.g.category,price)tenant(string, optional): Filter by tenantshowAllFields(boolean, optional): Include hidden fields (default:false)addEmpty(boolean, optional): Prepend an empty option to the list (default:false)specificPriceField(boolean, optional): Append the specific price field (default:false)
Response: Paginated collection of IndexField objects
Example:
GET /fields?filtergroup=category,price&tenant=default&showAllFields=true
List Filter Groups
Endpoint: GET /filter-groups
Operation ID: bundle_ecommerce_filter_groups_collection
Permission: bundle_ecommerce_back-office_order
Returns a list of all available filter groups for the product index.
Response: Paginated collection of FilterGroup objects
List Filter Field Values
Endpoint: GET /index/filter-field-values
Operation ID: ecommerce_index_get_filter_field_values_collection
Permission: bundle_ecommerce_back-office_order
Returns all distinct values for a given filter field, enabling filter configuration in the back office.
Query Parameters:
field(string, required): The filter field to retrieve values for (e.g.categoryIds)tenant(string, optional): The tenant to use for filtering (e.g.default)
Response: Paginated collection of FilterFieldValue objects
Possible Errors:
- 404 Not Found: The specified field does not exist in the index
Example:
GET /index/filter-field-values?field=categoryIds&tenant=default
Pricing Rules
List Pricing Rules
Endpoint: GET /pricing/rules
Operation ID: bundle_ecommerce_pricing_rules_collection
Permission: bundle_ecommerce_pricing_rules
Returns a list of all configured pricing rules with their basic properties.
Response: Paginated collection of PricingRuleListItem objects
Each item contains:
id(integer): Rule IDname(string): Internal rule namelabel(string): Localized display label (current locale)behavior(string): Rule behavior —additivorstopExecuteactive(boolean): Whether the rule is currently activeprio(integer): Display/execution priority
Get Pricing Rule
Endpoint: GET /pricing/rules/{id}
Operation ID: bundle_ecommerce_pricing_rules_item
Permission: bundle_ecommerce_pricing_rules
Returns the full details of a single pricing rule, including its localized labels and descriptions, condition tree, and action configurations.
Path Parameter:
id(integer): The ID of the pricing rule
Response: PricingRuleDetail object
The detail object contains:
id,name,behavior,active,prio— same as list itemlabel(object): Localized labels keyed by language code (e.g.{"en": "Summer Sale", "de": "Sommerschluss"})description(object): Localized descriptions keyed by language codecondition(object|null): Decoded condition tree, ornullif no condition is setactions(array): List of action configuration objects
Possible Errors:
- 404 Not Found: No pricing rule with the given ID exists
Example:
GET /pricing/rules/6
Get Pricing Config
Endpoint: GET /pricing/config
Operation ID: bundle_ecommerce_pricing_config
Permission: bundle_ecommerce_pricing_rules
Returns the list of all registered condition type keys and action type keys available for building pricing rule conditions and actions.
Response: PricingConfig object
conditions(string[]): Available condition type identifiers (e.g.CartAmount,Voucher,DateRange, ...)actions(string[]): Available action type identifiers (e.g.CartDiscount,Gift,FreeShipping, ...)
Create Pricing Rule
Endpoint: POST /pricing/rules
Operation ID: bundle_ecommerce_pricing_rules_create
Permission: bundle_ecommerce_pricing_rules
Creates a new pricing rule with the given name. The rule is created with default settings and must be fully configured via the save endpoint afterwards.
Request Body: JSON object
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Internal name for the rule. Must be non-empty, max 190 characters, and |
contain only letters, digits, -, and _. |
Response: JSON object with the ID of the newly created rule:
{ "id": 42 }
Possible Errors:
- 422 Unprocessable Entity: Name is empty, too long, contains invalid characters, or a rule with that name already exists
Example:
POST /pricing/rules
Content-Type: application/json
{ "name": "summer_sale" }
Save Pricing Rule
Endpoint: PUT /pricing/rules/{id}
Operation ID: bundle_ecommerce_pricing_rules_save
Permission: bundle_ecommerce_pricing_rules
Updates a pricing rule's full configuration, including behavior, active state, priority, localized labels and descriptions, condition tree, and actions. Returns the updated rule detail.
Path Parameter:
id(integer): The ID of the pricing rule to update
Request Body: JSON object (PricingRuleSaveParameters)
| Field | Type | Required | Description |
|---|---|---|---|
behavior | string | yes | additiv or stopExecute |
active | boolean | yes | Whether the rule is active |
prio | integer | yes | Execution/display priority |
label | object | yes | Localized labels keyed by language code (e.g. {"en": "Summer Sale"}) |
description | object | yes | Localized descriptions keyed by language code |
condition | object|null | no | Condition tree as a decoded JSON object, or null to remove |
actions | array | yes | List of action configuration objects |
Response: Updated PricingRuleDetail object
Possible Errors:
- 404 Not Found: No pricing rule with the given ID exists
Example:
PUT /pricing/rules/6
Content-Type: application/json
{
"behavior": "stopExecute",
"active": true,
"prio": 1,
"label": { "en": "Summer Sale", "de": "Sommerschluss" },
"description": { "en": "10% off all products" },
"condition": {
"type": "Pimcore\\Bundle\\EcommerceFrameworkBundle\\PricingManager\\Condition\\CartAmount",
"config": { "limit": 100 }
},
"actions": [{
"type": "Pimcore\\Bundle\\EcommerceFrameworkBundle\\PricingManager\\Action\\CartDiscount",
"config": { "percent": 10 }
}]
}
Delete Pricing Rule
Endpoint: DELETE /pricing/rules/{id}
Operation ID: bundle_ecommerce_pricing_rules_delete
Permission: bundle_ecommerce_pricing_rules
Permanently deletes a pricing rule.
Path Parameter:
id(integer): The ID of the pricing rule to delete
Response: HTTP 200 OK (empty body)
Possible Errors:
- 404 Not Found: No pricing rule with the given ID exists
Example:
DELETE /pricing/rules/6
Copy Pricing Rule
Endpoint: POST /pricing/rules/{id}/copy
Operation ID: bundle_ecommerce_pricing_rules_copy
Permission: bundle_ecommerce_pricing_rules
Creates a duplicate of an existing pricing rule. The copy is given an auto-generated name based on the original.
Path Parameter:
id(integer): The ID of the pricing rule to copy
Response: HTTP 200 OK (empty body)
Possible Errors:
- 404 Not Found: No pricing rule with the given ID exists
Example:
POST /pricing/rules/6/copy
Rename Pricing Rule
Endpoint: PUT /pricing/rules/{id}/rename
Operation ID: bundle_ecommerce_pricing_rules_rename
Permission: bundle_ecommerce_pricing_rules
Renames an existing pricing rule's internal name.
Path Parameter:
id(integer): The ID of the pricing rule to rename
Request Body: JSON object
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | New internal name. Same validation rules as for create: non-empty, |
max 190 characters, letters/digits/-/_ only. |
Response: HTTP 200 OK (empty body)
Possible Errors:
- 404 Not Found: No pricing rule with the given ID exists
- 422 Unprocessable Entity: Name is empty, too long, contains invalid characters, or a rule with that name already exists
Example:
PUT /pricing/rules/6/rename
Content-Type: application/json
{ "name": "winter_sale" }
Save Pricing Rule Order
Endpoint: PUT /pricing/rules/order
Operation ID: bundle_ecommerce_pricing_rules_save_order
Permission: bundle_ecommerce_pricing_rules
Updates the priority (display and execution order) of multiple pricing rules at once. Rules not included in the map are left unchanged. Unknown IDs are silently ignored.
Request Body: JSON object
| Field | Type | Required | Description |
|---|---|---|---|
rules | object | yes | Map of rule ID (string key) to new priority value (integer). |
Response: HTTP 200 OK (empty body)
Example:
PUT /pricing/rules/order
Content-Type: application/json
{
"rules": { "6": 0, "7": 1, "8": 2, "9": 3, "10": 4 }
}
Permissions
| Constant | Value | Used For |
|---|---|---|
BUNDLE_ECOMMERCE_BACK_OFFICE_ORDER | bundle_ecommerce_back-office_order | Index endpoints |
BUNDLE_ECOMMERCE_PRICING_RULES | bundle_ecommerce_pricing_rules | Pricing rule endpoints |
Common Response Codes
- 200 OK: Successful request
- 401 Unauthorized: Missing or invalid authentication
- 404 Not Found: Resource not found
- 422 Unprocessable Entity: Validation failure (e.g. invalid or duplicate rule name)
API Tags
All endpoints are tagged with Ecommerce for OpenAPI documentation grouping.
Usage Examples
Managing Pricing Rules
-
List all rules:
GET /pricing/rules -
Get full details of a rule:
GET /pricing/rules/6 -
Create a new rule:
POST /pricing/rules
{ "name": "black_friday" } -
Configure the new rule:
PUT /pricing/rules/42
{
"behavior": "stopExecute", "active": true, "prio": 0,
"label": { "en": "Black Friday" }, "description": {}, "actions": [...]
} -
Reorder all rules:
PUT /pricing/rules/order
{ "rules": { "42": 0, "6": 1, "7": 2 } } -
Copy an existing rule as a starting point:
POST /pricing/rules/6/copy -
Delete a rule:
DELETE /pricing/rules/6
Browsing Index Data
-
List all tenants:
GET /tenants -
List fields for a specific tenant:
GET /fields?tenant=default -
List all filter groups:
GET /filter-groups -
Get all values for a filter field:
GET /index/filter-field-values?field=categoryIds&tenant=default
Orders
The order endpoints are split into two groups:
- View endpoints return rendered HTML (Twig templates) intended for iframe embedding in the
Pimcore Studio UI. They respond with
Content-Type: text/html. - Mutation endpoints perform actions (cancel, edit, complaint) and return an empty
200 OK.
Order Views
List Orders
Endpoint: GET /orders/view/list
Operation ID: bundle_ecommerce_orders_view_list
Permission: bundle_ecommerce_back-office_order
Returns a rendered HTML page listing orders with pagination, filtering by date range, search
query, and pricing rule. The response is the rendered list.html.twig template.
Query Parameters:
type(string, optional): List type —order(default) ororder_itemq(string, optional): Free-text search querysearch(string, optional): Search scope —order(default) orproductTypefrom(string, optional): Filter from date inY-m-dformat (e.g.2024-01-01). Defaults to first day of current month if neitherfromnortillis provided.till(string, optional): Filter till date inY-m-dformat (e.g.2024-12-31)pricingRule(integer, optional): Filter by pricing rule IDpage(integer, optional): Page number, 1-based (default:1)limit(integer, optional): Number of results per page (default:10)
Response: text/html — rendered order list page
Example:
GET /orders/view/list?type=order&q=Jane&from=2024-01-01&till=2024-12-31&page=1&limit=10
View Order Detail
Endpoint: GET /orders/{id}/view/detail
Operation ID: bundle_ecommerce_orders_view_detail
Permission: bundle_ecommerce_back-office_order
Returns a rendered HTML page showing the full details of a single order, including order items,
payment information, invoice/delivery address geocoding, a chronological change log timeline,
and customer account summary. The response is the rendered detail.html.twig template.
Path Parameter:
id(integer): The ID of the order
Response: text/html — rendered order detail page
Possible Errors:
- 404 Not Found: No order with the given ID exists
Example:
GET /orders/42/view/detail
View Order Item Cancel Form
Endpoint: GET /orders/items/{id}/view/cancel
Operation ID: bundle_ecommerce_orders_view_item_cancel
Permission: bundle_ecommerce_back-office_order
Returns a rendered HTML form for cancelling an order item. The response is the rendered
item_cancel.html.twig template, which displays the item details and a cancellation form.
Path Parameter:
id(integer): The ID of the order item
Response: text/html — rendered cancel form
Possible Errors:
- 404 Not Found: No order item with the given ID exists
Example:
GET /orders/items/5/view/cancel
View Order Item Edit Form
Endpoint: GET /orders/items/{id}/view/edit
Operation ID: bundle_ecommerce_orders_view_item_edit
Permission: bundle_ecommerce_back-office_order
Returns a rendered HTML form for editing the quantity of an order item. The response is the
rendered item_edit.html.twig template.
Path Parameter:
id(integer): The ID of the order item
Response: text/html — rendered edit form
Possible Errors:
- 404 Not Found: No order item with the given ID exists
Example:
GET /orders/items/5/view/edit
View Order Item Complaint Form
Endpoint: GET /orders/items/{id}/view/complaint
Operation ID: bundle_ecommerce_orders_view_item_complaint
Permission: bundle_ecommerce_back-office_order
Returns a rendered HTML form for filing a complaint about an order item. The response is the
rendered item_complaint.html.twig template.
Path Parameter:
id(integer): The ID of the order item
Response: text/html — rendered complaint form
Possible Errors:
- 404 Not Found: No order item with the given ID exists
Example:
GET /orders/items/5/view/complaint
Order Item Mutations
Cancel Order Item
Endpoint: POST /orders/items/{id}/cancel
Operation ID: bundle_ecommerce_orders_item_cancel_post
Permission: bundle_ecommerce_back-office_order
Cancels the specified order item. An optional message can be attached to the cancellation note
for audit trail purposes. Returns an empty 200 OK response on success.
Path Parameter:
id(integer): The ID of the order item to cancel
Request Body: JSON object (OrderItemCancelParameters)
| Field | Type | Required | Description |
|---|---|---|---|
message | string | no | Optional message to attach to the cancel note (default: empty string) |
Response: Empty 200 OK
Possible Errors:
- 404 Not Found: No order item with the given ID exists
Example:
POST /orders/items/5/cancel
Content-Type: application/json
{ "message": "Customer requested cancellation." }
Edit Order Item
Endpoint: PUT /orders/items/{id}/edit
Operation ID: bundle_ecommerce_orders_item_edit_put
Permission: bundle_ecommerce_back-office_order
Updates the quantity of the specified order item. An optional message can be attached to the edit
note for audit trail purposes. Returns an empty 200 OK response on success.
Path Parameter:
id(integer): The ID of the order item to edit
Request Body: JSON object (OrderItemEditParameters)
| Field | Type | Required | Description |
|---|---|---|---|
quantity | number (float) | yes | New quantity for the order item |
message | string | no | Optional message to attach to the edit note (default: empty string) |
Response: Empty 200 OK
Possible Errors:
- 404 Not Found: No order item with the given ID exists
Example:
PUT /orders/items/5/edit
Content-Type: application/json
{ "quantity": 3.0, "message": "Customer changed mind." }
File Order Item Complaint
Endpoint: POST /orders/items/{id}/complaint
Operation ID: bundle_ecommerce_orders_item_complaint_post
Permission: bundle_ecommerce_back-office_order
Files a complaint for the specified order item for a given quantity. An optional message can be
attached to the complaint note for audit trail purposes. Returns an empty 200 OK response on success.
Path Parameter:
id(integer): The ID of the order item to complain about
Request Body: JSON object (OrderItemComplaintParameters)
| Field | Type | Required | Description |
|---|---|---|---|
quantity | number (float) | yes | Quantity subject to the complaint |
message | string | no | Optional message to attach to the complaint note (default: empty string) |
Response: Empty 200 OK
Possible Errors:
- 404 Not Found: No order item with the given ID exists
Example:
POST /orders/items/5/complaint
Content-Type: application/json
{ "quantity": 1.0, "message": "Item arrived damaged." }
Context-Aware Routing and Form Interception
The order Twig templates are shared between the legacy admin interface and the Pimcore Studio
iframe context. Since the Studio iframe operates under the pimcore-studio firewall, all URLs
within the templates must resolve to Studio routes when rendered in that context.
How It Works
The OrderViewService passes studioContext: true to every twig->render() call. Each
template checks {% set studioContext = studioContext|default(false) %} and conditionally
switches route names:
- View links (list, detail, edit form, cancel form, complaint form) use
pimcore_studio_api_ecommerce_orders_view_*routes in Studio context andpimcore_ecommerce_backend_admin-order_*routes in the legacy context. - Pagination (
paging.html.twig) usespimcore_url()which resolves relative to the current route — no changes needed. - List search form has no explicit
actionattribute and submits to the current URL — works in both contexts without changes.
JavaScript Form Interception for Mutations
The Studio mutation endpoints (cancel, edit, complaint) expect JSON request bodies and use
#[MapRequestPayload] for deserialization. Traditional HTML form POST submissions send
application/x-www-form-urlencoded data, which is incompatible.
In Studio context, the item form templates (cancel, edit, complaint) add data-studio-context,
data-studio-method, and data-studio-redirect attributes to the <form> element. A <script>
block intercepts the form submit event and:
- Prevents the default form submission.
- Collects form field values into a JSON object matching the mutation endpoint's schema.
- Sends the JSON payload via
fetch()to the mutation endpoint URL using the correct HTTP method (POSTfor cancel/complaint,PUTfor edit). - On a successful response, redirects the browser to the order detail view page.
This approach keeps the mutation controllers as clean JSON APIs with no HTML rendering concerns. When the templates are eventually replaced with a dedicated Studio UI, the mutation endpoints remain unchanged.
CSRF Token Handling
In legacy context, forms include a hidden csrfToken field validated by the legacy admin
controller. In Studio context, authentication is handled by the Studio firewall session, so
the CSRF token input is conditionally omitted:
{% if not studioContext %}
<input type="hidden" name="csrfToken" value="{{ pimcore_csrf.getCsrfToken(app.request.session) }}">
{% endif %}
Vouchers
The voucher endpoints manage voucher series tokens — generating, exporting, and cleaning up tokens and reservations. Like the order endpoints, they are split into:
- View endpoint returning rendered HTML (Twig template) for iframe embedding.
- Mutation endpoints performing actions and returning an empty
200 OK.
Voucher Views
View Voucher Code Tab
Endpoint: GET /vouchers/{id}/view/tab
Operation ID: bundle_ecommerce_vouchers_view_tab
Permission: bundle_ecommerce_pricing_rules
Returns a rendered HTML page showing the voucher code tab for a voucher series, including the token listing with pagination, filter controls, statistics, and action modals (generate, cleanup, cleanup reservations). Renders either the pattern or single token template depending on the token manager type.
Path Parameter:
id(integer): The ID of the voucher series
Query Parameters:
page(integer, optional): Page number, 1-based (default:1)tokensPerPage(integer, optional): Number of tokens per page (default:25)token(string, optional): Filter by token codecreation_from(string, optional): Filter from date inY-m-dformatcreation_to(string, optional): Filter to date inY-m-dformatusages(integer, optional): Filter by usage countlength(integer, optional): Filter by token lengthsort_criteria(string, optional): Sort field (e.g.token,usages,length,timestamp)sort_order(string, optional): Sort direction:ASCorDESC
Response: text/html — rendered voucher code tab page
Possible Errors:
- 404 Not Found: No voucher series with the given ID exists
Example:
GET /vouchers/42/view/tab?page=1&tokensPerPage=25&sort_criteria=timestamp&sort_order=DESC
Voucher Token Operations
Export Tokens
Endpoint: GET /vouchers/{id}/tokens/export
Operation ID: bundle_ecommerce_vouchers_tokens_export
Permission: bundle_ecommerce_pricing_rules
Exports all voucher tokens for a series as a file download. The token manager must implement
ExportableTokenManagerInterface.
Path Parameter:
id(integer): The ID of the voucher series
Query Parameters:
format(string, optional): Export format —csv(default) orplain
Response: File download (text/csv or text/plain)
Possible Errors:
- 404 Not Found: No voucher series with the given ID exists
- 500 Environment Error: Token manager does not support exporting
Example:
GET /vouchers/42/tokens/export?format=csv
Generate Tokens
Endpoint: POST /vouchers/{id}/tokens/generate
Operation ID: bundle_ecommerce_vouchers_tokens_generate
Permission: bundle_ecommerce_pricing_rules
Generates or updates voucher tokens for a series based on the configured token manager settings.
Path Parameter:
id(integer): The ID of the voucher series
Response: Empty 200 OK
Possible Errors:
- 404 Not Found: No voucher series with the given ID exists
- 500 Environment Error: Token generation failed
Example:
POST /vouchers/42/tokens/generate
Cleanup Tokens
Endpoint: POST /vouchers/{id}/tokens/cleanup
Operation ID: bundle_ecommerce_vouchers_tokens_cleanup
Permission: bundle_ecommerce_pricing_rules
Cleans up voucher tokens by usage status, with an optional date filter to only remove tokens older than a given date.
Path Parameter:
id(integer): The ID of the voucher series
Request Body: JSON object (CleanupTokensParameters)
| Field | Type | Required | Description |
|---|---|---|---|
usage | string | yes | Which tokens to clean up: used, unused, or both |
olderThan | string | no | Only remove tokens older than this date (format: Y-m-d) |
Response: Empty 200 OK
Possible Errors:
- 404 Not Found: No voucher series with the given ID exists
- 500 Environment Error: Token cleanup failed
Example:
POST /vouchers/42/tokens/cleanup
Content-Type: application/json
{ "usage": "used", "olderThan": "2024-01-01" }
Cleanup Reservations
Endpoint: POST /vouchers/{id}/reservations/cleanup
Operation ID: bundle_ecommerce_vouchers_reservations_cleanup
Permission: bundle_ecommerce_pricing_rules
Cleans up voucher token reservations that are older than a specified duration in minutes.
Path Parameter:
id(integer): The ID of the voucher series
Request Body: JSON object (CleanupReservationsParameters)
| Field | Type | Required | Description |
|---|---|---|---|
duration | integer | yes | Remove reservations older than this many minutes |
Response: Empty 200 OK
Possible Errors:
- 404 Not Found: No voucher series with the given ID exists
- 500 Environment Error: Reservation cleanup failed
Example:
POST /vouchers/42/reservations/cleanup
Content-Type: application/json
{ "duration": 5 }
Context-Aware Routing for Voucher Templates
The voucher Twig templates use the same context-aware routing pattern as the order templates.
The VoucherViewService passes studioContext: true to every render call.
Route Mapping
| Legacy Route | Studio Route |
|---|---|
pimcore_ecommerce_backend_voucher_export-tokens | pimcore_studio_api_ecommerce_vouchers_tokens_export |
pimcore_ecommerce_backend_voucher_generate | pimcore_studio_api_ecommerce_vouchers_tokens_generate |
pimcore_ecommerce_backend_voucher_cleanup | pimcore_studio_api_ecommerce_vouchers_tokens_cleanup |
pimcore_ecommerce_backend_voucher_cleanup-reservations | pimcore_studio_api_ecommerce_vouchers_reservations_cleanup |
Generate and Assign Modals
The generate modal (pattern/generate_modal.html.twig) and assign settings modal
(single/assign_settings_modal.html.twig) use <a> links that in legacy context perform a
GET redirect. In Studio context, the link targets the Studio POST endpoint and a JavaScript
click interceptor sends the request via fetch() then redirects to the tab view.
Cleanup Form Modals
The cleanup tokens modal and cleanup reservations modal use the same form interception pattern
as the order item mutations: in Studio context, data-studio-context, data-studio-method,
and data-studio-redirect attributes are added to the form, and a script intercepts the
submit event to send a JSON fetch() request instead of the traditional form POST. CSRF tokens
are conditionally omitted in Studio context.