Permissions and roles

Every E-commerce permission key, the three separate gates a request passes, the keys that are grantable but wired to nothing, and role recipes that actually work.

8 min readUpdated 6 August 2026permissions, roles, access-control, security

E-commerce adds 33 permission keys that something reads, out of 36 the role editor will let you tick. A Super Admin holds everything unconditionally and needs none of them — and is also the only person who can grant them to anyone else. Every other role holds nothing until a Super Admin grants it.

Install and enable says "26 permission keys". That is the subset carried in backend route metadata. There are another 7 that gate the admin screens rather than the API, which is why a role built from the 26 alone lands on a store with no way in.

How a key is shaped

A permission key is the admin route path, with / turned into ., prefixed by a verb:

Verb Gates
access. Opening a screen. Read by the admin menu and by the page gate
view. GET — reading the list, one record, or the option dropdowns
create. POST that creates a record
edit. PUT, including a record's status — which is changed in the row's edit form, not on the row itself
delete. DELETE, single or bulk

So /admin/ecommerce/product produces access.ecommerce.product, view.ecommerce.product, create.ecommerce.product, edit.ecommerce.product and delete.ecommerce.product. There are no manage.* keys in E-commerce.

The three gates

A single click on a store screen is judged in three separate places. They read different keys, and each fails in a way that looks nothing like the other two.

  1. The URL gate. Before the page renders, the frontend matches the admin path against a generated manifest and requires the access.* key listed there. Seven E-commerce paths are in that manifest — product, category, order, review, wishlist, discount, shipping. Any admin path that is not in the manifest falls back to requiring access.admin, which is what gates /admin/ecommerce (the dashboard), /admin/ecommerce/settings and /admin/ecommerce/order/<id>.

  2. The screen gate. Every store list screen is a data table that carries its own access and view keys. It issues no request at all unless both pass.

  3. The API gate. The backend checks the route's own key against the caller's role. A miss answers Forbidden - You do not have permission to access this — regardless of what the first two gates allowed.

What each half looks like when it is missing

Granted What the operator sees
Only access.ecommerce.product The page opens and draws its header, toolbar and analytics — then shows an empty table with "no results". No list request was ever sent. It reads as an empty catalogue, not as a permissions problem
Only view/create/edit/delete.ecommerce.product No link to the store anywhere in the admin nav, and typing the URL bounces you. The menu entry for E-commerce is gated on access.ecommerce.category
access.* but no access.admin The list screens open; the dashboard, the settings screen and the per-order screen do not, and the Analytics tab fails to load on the six tables that have one
view.* but no edit.* The Edit entry in the row's ⋯ menu is still listed, but greyed out with a "You don't have permission to edit items" tooltip, and the Save button in the form is disabled. Nothing on a row can be flipped directly — wherever a store table shows a status, it is a read-only badge

access.ecommerce.<resource> and view.ecommerce.<resource> are not alternatives. A working role holds the access. key for every screen it should open and the verb keys for every action it should take, plus access.admin.

The complete list

Screen keys (7)

These exist only as page gates. No backend route reads them.

Key Opens
access.ecommerce.product /admin/ecommerce/product
access.ecommerce.category /admin/ecommerce/categoryalso the admin menu entry for the whole store
access.ecommerce.order /admin/ecommerce/order
access.ecommerce.review /admin/ecommerce/review
access.ecommerce.wishlist /admin/ecommerce/wishlist
access.ecommerce.discount /admin/ecommerce/discount
access.ecommerce.shipping /admin/ecommerce/shipping

API keys (26)

These are declared on the backend routes themselves.

Resource access view create edit delete
dashboard access.ecommerce.dashboard
product
category
discount
shipping
order none
review none
wishlist none

access.ecommerce.dashboard is the odd one out: it is an access. key that guards an API route (GET /api/admin/ecommerce/dashboard) rather than a screen. The dashboard screen at /admin/ecommerce is guarded by access.admin, so both are needed to see figures.

create.ecommerce.order, create.ecommerce.review and create.ecommerce.wishlist appear in the role editor and can be ticked. There is no admin create door for any of them — no POST route exists, and no create button is rendered on those three screens. Orders come from checkout, reviews and wishlists come from customers.

Granting them is harmless and achieves nothing. That is the gap between the 36 tickboxes and the 33 keys that are read.

Settings is not an E-commerce permission

/admin/ecommerce/settings looks like a store screen and is reached from the store menu, but it saves through core's platform settings endpoint (PUT /api/admin/system/settings). That endpoint requires edit.settings.

So a role holding all 33 E-commerce keys cannot change the tax rate, the shipping fee or the products-per-page value. It can open the screen — the URL falls through to access.admin — type new values, press Save, and be refused by the API. Nothing on the screen hides the Save button first.

Grant edit.settings to whoever owns store pricing, and understand that it is the platform-wide settings permission: it also covers mail, branding and everything else on /admin/system/settings. There is no store-only settings permission. The keys themselves are on Store settings and dashboard.

Keys from outside E-commerce that the store screens need

Three admin screens call endpoints that belong to other parts of the platform. A role without these keys sees the screen work until it hits the missing piece.

Screen Also calls Key needed Symptom if missing
Product create/edit form /api/admin/finance/wallet/options view.wallet The Wallet Type dropdown is empty — you cannot save a product
Product create/edit form /api/admin/finance/currency/options view.fiat.currency The Currency dropdown is empty for every wallet type
Product create/edit form /api/admin/ecommerce/category/options view.ecommerce.category The Category dropdown is empty
Discount create/edit form /api/admin/ecommerce/product/options view.ecommerce.product The Product picker is empty — a discount must name a product
Review screen /api/admin/ecommerce/product/options view.ecommerce.product The product filter and edit form have nothing to pick
The Analytics tab on six of the seven store tables — every one except Categories, which has none /api/admin/analysis access.admin The KPI cards and charts fail to load

The cross-resource ones are the traps. A "catalogue editor" holding only the product keys cannot pick a category; a "promotions" role holding only the discount keys cannot pick a product to discount.

The two money permissions

It is not a fulfilment permission. It covers the status change that moves an order to CANCELLED or REJECTED, and that transition:

  • debits your platform wallet to reverse the store revenue,
  • reverses the shipping-and-tax pass-through credit,
  • credits the buyer the full original charge,
  • restores the stock.

The same key also covers assigning shipments, correcting shipping addresses, and attaching licence keys and download links to somebody's purchase. There is no way to separate "mark this order shipped" from "refund this order" — they are one key. Treat it as a finance permission and give it to the people you would trust with a withdrawal approval.

delete.ecommerce.order is the safer of the two, because the delete doors refuse an order that has been paid for and not refunded. The request is blocked by id with an explicit instruction to cancel first. So delete cannot silently strand a buyer's money — but it can only be used after someone with edit.ecommerce.order has done the refund. The full behaviour is on Orders and fulfilment.

Bulk actions carry the same keys as their single-record equivalents. A bulk cancel from the orders table runs the identical refund path, once per order.

Granting them

Permissions attach to roles, and roles attach to users. There is no per-user override.

  1. Open the role editor. CRM → Roles & Permissions → User Roles (/admin/crm/role). access.role and view.role open the screen and edit.role gets a save past the permission gate — but only a Super Admin can actually save. Every role write door re-checks the caller's role name after the permission check and refuses anyone else: updating a role answers Forbidden - Only Super Admins can update roles, saving its permission set answers Forbidden - Only Super Admins can sync role permissions, and creating or deleting a role carries the same check. Granting E-commerce permissions cannot be delegated to an admin who is not a Super Admin, no matter which role keys they hold.

  2. Edit the role you want to change and tick the keys from the recipes below. All 36 E-commerce keys are listed by name.

  3. Save. The change is written and the role cache is refreshed.

  4. Have the holder sign out and back in. Their session carries the role, and the frontend caches roles and permissions for five minutes.

/admin/crm/permission is read-only — it lists what exists so you can see the names. Permission rows come from the platform's seed data. If a key you expect is not listed, it was never seeded, which means no role can hold it and the route behind it is Super-Admin-only.

Role recipes

Each of these includes both halves of the gate. access.admin is in all of them because without it the dashboard, the per-order screen and every Analytics tab are closed.

Read-only reporting

Sees the numbers, changes nothing.

access.admin
access.ecommerce.dashboard
access.ecommerce.order      view.ecommerce.order
access.ecommerce.product    view.ecommerce.product
access.ecommerce.category   view.ecommerce.category

No edit. or delete. keys at all, so the Edit and Delete entries in every row menu are greyed out or absent: no status change, no refund, no destructive action anywhere in the store.

Catalogue editor

Builds and maintains the catalogue. Cannot see an order or touch money.

access.admin
access.ecommerce.product    view/create/edit/delete.ecommerce.product
access.ecommerce.category   view/create/edit/delete.ecommerce.category
view.wallet
view.fiat.currency

view.wallet and view.fiat.currency are not optional here — without them the product form's Wallet Type and Currency dropdowns come back empty and the product cannot be saved. See Categories and products.

Fulfilment

Works the order queue and the shipment records.

access.admin
access.ecommerce.order      view.ecommerce.order  edit.ecommerce.order
access.ecommerce.shipping   view/create/edit.ecommerce.shipping
access.ecommerce.product    view.ecommerce.product

edit.ecommerce.order is what lets them mark an order complete, assign a shipment and fix an address — and it is the same key that cancels an order and returns the buyer's money from your platform wallet. If that is not acceptable, there is no smaller grant that keeps fulfilment working.

create.ecommerce.shipping is there because assigning a shipment links an existing shipping record to the order; somebody has to create it first. See Shipping and fulfilment records.

Review moderation

Publishes and unpublishes reviews.

access.admin
access.ecommerce.review     view/edit/delete.ecommerce.review
access.ecommerce.product    view.ecommerce.product

Moderation happens in the row's edit form: the Status field there is what edit.ecommerce.review gates, and it moves a review between Approved and Pending. The status column on the table is a read-only badge — there is nothing to click on the row. view.ecommerce.product is needed for the product filter and the edit form's product field. There is no create.ecommerce.review door: an operator cannot write a review.

Promotions

Runs discount codes.

access.admin
access.ecommerce.discount   view/create/edit/delete.ecommerce.discount
access.ecommerce.product    view.ecommerce.product

Every discount names one product, so view.ecommerce.product is required to create a usable code at all. See Discount codes.

Store manager

Everything except the money door and the settings.

Take the catalogue editor, fulfilment, moderation and promotions sets together, add access.ecommerce.wishlist with view.ecommerce.wishlist, and decide separately whether to add edit.settings. Whoever holds edit.settings can change the tax rate for the whole store, so it is worth keeping on one person.

Diagnosing a permission problem

Symptom Almost always
No E-commerce entry in the admin menu Missing access.ecommerce.category
Menu entry present, page bounces or shows an unauthorised overlay Missing access.admin, on a path not in the screen manifest
Page opens, table permanently shows "no results" access.* granted, view.* not — no request is being made
Forbidden - You do not have permission to access this in the response The route's own key is missing from the role
A dropdown in a form is empty The cross-resource key for that dropdown — see the table above
Save on the settings screen is refused Missing edit.settings, which is not an E-commerce key
A key you want is not listed in the role editor It was never seeded; the route behind it is Super-Admin-only

A permission refusal answers HTTP 403, with the reason — Forbidden - You do not have permission to access this — in the response body as {message, statusCode}. Both the status code and the body carry it, so either one is safe to branch on. The endpoint list is on API and data. Broader failures are on Troubleshooting.