ERP API Reference
The plugin's ERP modules (Invoicing + myDATA, Inventory/WMS, Purchasing, Accounting/GL, CRM, HR) are exposed over the same WordPress REST namespace as the rest of the commerce API:
/wp-json/ec/v1/…
There is no separate ERP namespace — every route below lives under ec/v1. What sets the ERP routes apart is the three-layer access gate in front of them. This page is the domain-grouped index of the ERP surface plus the exact gate each group enforces.
The REST API Overview lists the ERP groups as part of the full ec/v1 map. This page is the ERP-focused expansion — same endpoints, but annotated with the license feature and ERP role capability each group requires. For the exhaustive, machine-readable list of paths, methods, parameters, and schemas, always defer to the OpenAPI spec.
The ERP access gate (read this first)
Every ERP route passes through up to three checks, in order. A failure at any layer short-circuits with a 403 before the handler runs.
1. WordPress capability — access_erp
The user must hold the access_erp capability (granted through ERP roles) or manage_options (administrators bypass ERP roles entirely). This is the coarse "can this user touch the ERP at all" gate.
2. License feature — Business tier
Each domain maps to one license feature, and all ERP features require an active Business-tier license. When the store's tier doesn't cover the feature, the route returns:
{
"code": "license_required",
"message": "This feature requires a Business license or higher.",
"data": {
"status": 403,
"required_feature": "erp_inventory",
"required_tier": "business",
"current_tier": "starter"
}
}
| ERP domain | License feature | RBAC module |
|---|---|---|
| Invoicing + myDATA | erp_invoicing | invoicing |
| Inventory / WMS | erp_inventory | inventory |
| Purchasing | erp_purchasing | purchasing |
| Accounting / GL | erp_accounting | accounting |
| CRM | erp_crm | crm |
| HR | erp_hr | hr |
3. ERP role capability — {module}.{action}
On top of the license check, the user's ERP role must grant the specific module permission. Permissions follow a {module}.{action} shape, where the module is the license feature minus its erp_ prefix and the action is derived from the HTTP method:
GET→{module}.view- any write (
POST/PUT/PATCH/DELETE) →{module}.update(v1 enforcement is module-level — a role that holds a module can perform every write in it; the exact write verb is not separately gated).
A role that lacks the module returns:
{
"code": "erp_role_forbidden",
"message": "Your ERP role does not permit \"inventory.update\".",
"data": {
"status": 403,
"required_permission": "inventory.update",
"role": "sales"
}
}
ERP roles and the modules they manage:
| Role | Manager access to modules |
|---|---|
| admin | All modules (bypasses the role matrix) |
| manager | invoicing, inventory, purchasing, crm |
| accountant | invoicing, accounting |
| warehouse | inventory, purchasing |
| sales | invoicing, crm |
| hr_manager | hr |
| employee | None — self-service (ESS) only; see HR self-service |
| viewer | Read-only (.view) on all modules except HR |
Two cross-cutting resources are not tied to a single module:
dashboard— the ERP SPA landing read;.viewis available to every authenticated ERP role.customers— shared invoicing + CRM master data; any role holding eitherinvoicingorcrmgets it.
HR is deliberately excluded from the viewer read-all blanket — it holds sensitive employee PII (ΑΦΜ/ΑΜΚΑ/IBAN/emergency contacts), so only hr_manager and admin see manager-HR data. Every user sees their own record through the ESS surface.
ERP controllers compose these checks through the LicenseGatedErp trait (src/Licensing/Traits/LicenseGatedErp.php). The common case is one call — erpModulePermissionCheck('erp_<module>', $request) — as the route's permission_callback, which runs all three layers. Admin-only surfaces (ERP config, ΕΡΓΑΝΗ) use requireErpAdmin(), which requires the ERP role admin explicitly (never the viewer blanket).
Invoicing + myDATA — erp_invoicing
Greek e-invoicing with AADE myDATA transmission. Controller: InvoiceApiController.
Path (under ec/v1) | Methods | Purpose |
|---|---|---|
/invoices | GET, POST | List / create invoices |
/invoices/stats | GET | Aggregate invoice stats |
/invoices/{id} | GET, PUT, DELETE | Read / update / delete an invoice |
/invoices/{id}/issue | POST | Finalize (issue) a draft |
/invoices/{id}/cancel | POST | Cancel an issued invoice |
/invoices/from-order/{orderId} | POST | Generate an invoice from an order |
/invoices/{id}/transmit | POST | Transmit to myDATA (AADE) |
/invoices/{id}/cancel-mydata | POST | Cancel a prior myDATA transmission |
/invoices/{id}/mydata-log | GET | myDATA transmission log for the invoice |
/invoices/{id}/mydata-preview | GET | Preview the myDATA XML payload |
/invoices/{id}/pdf | GET | Download the invoice PDF |
/invoices/{id}/pdf/generate | POST | (Re)generate the PDF |
/invoice-series | GET, POST | List / create numbering series |
/invoice-series/{id} | PUT, DELETE | Update / delete a series |
/invoices/settings | GET, PUT | Invoicing + myDATA settings |
/invoices/mydata/test | POST | Test the myDATA connection (admin — manage_options) |
/invoices/reference-data | GET | myDATA reference tables (VAT categories, types, etc.) |
Gate: access_erp + erp_invoicing (Business) + invoicing.{view|update}. The myDATA connection test additionally requires manage_options.
Inventory / WMS — erp_inventory
Multi-warehouse stock, movements, transfers, stocktakes. Controllers: WarehouseApiController, StockApiController.
Path (under ec/v1) | Methods | Purpose |
|---|---|---|
/warehouses | GET, POST | List / create warehouses |
/warehouses/{id} | GET, PUT, DELETE | Read / update / delete a warehouse |
/warehouses/{id}/set-default | POST | Mark as the default warehouse |
/warehouses/{id}/activate | POST | Activate |
/warehouses/{id}/deactivate | POST | Deactivate |
/stock/levels | GET | Stock levels across warehouses |
/stock/levels/{product_id} | GET | Stock levels for one product |
/stock/adjust | POST | Adjust stock (with a reason/movement) |
/stock/transfer | POST | Transfer stock between warehouses |
/stock/stocktake | POST | Submit a stocktake / reconciliation |
/stock/movements | GET | Movement ledger |
/stock/movements/{product_id} | GET | Movement ledger for one product |
/stock/stats | GET | Inventory KPIs |
Gate: access_erp + erp_inventory (Business) + inventory.{view|update}.
Purchasing — erp_purchasing
Suppliers, purchase orders, goods received, supplier pricing. Controllers: SupplierApiController, PurchaseApiController.
Path (under ec/v1) | Methods | Purpose |
|---|---|---|
/suppliers | GET, POST | List / create suppliers |
/suppliers/{id} | GET, PUT, DELETE | Read / update / delete a supplier |
/suppliers/{id}/prices | GET | Supplier price list |
/purchase-orders | GET, POST | List / create purchase orders |
/purchase-orders/stats | GET | PO KPIs |
/purchase-orders/auto-reorder | POST | Generate POs from reorder points |
/purchase-orders/{id} | GET, PUT, DELETE | Read / update / delete a PO |
/purchase-orders/{id}/send | POST | Email the PO to the supplier |
/purchase-orders/{id}/resend | POST | Re-send the PO email |
/purchase-orders/{id}/receive | POST | Record receipt (creates a goods-received note) |
/goods-received | GET, POST | List / create goods-received notes |
/goods-received/{id} | GET | Read a goods-received note |
/supplier-prices | GET, POST | Supplier-price CRUD |
/supplier-prices/{id} | PUT, DELETE | Update / delete a supplier price |
Gate: access_erp + erp_purchasing (Business) + purchasing.{view|update}.
Accounting / GL — erp_accounting
Chart of accounts, fiscal years, double-entry journal, and the six financial reports. Controller: AccountingApiController.
Path (under ec/v1) | Methods | Purpose |
|---|---|---|
/accounts | GET, POST | List / create GL accounts |
/accounts/tree | GET | Chart of accounts as a tree |
/accounts/seed-elp | POST | Seed the Greek chart of accounts (ΕΛΠ) |
/accounts/{id} | GET, PUT, DELETE | Read / update / delete an account |
/fiscal-years | GET, POST | List / create fiscal years |
/fiscal-years/current | GET | The active fiscal year |
/fiscal-years/{id} | GET, PUT | Read / update a fiscal year |
/fiscal-years/{id}/close | POST | Close a fiscal year |
/journal-entries | GET, POST | List / create journal entries |
/journal-entries/stats | GET | Journal KPIs |
/journal-entries/{id} | GET, PUT, DELETE | Read / update / delete an entry |
/journal-entries/{id}/post | POST | Post (finalize) an entry |
/journal-entries/{id}/reverse | POST | Reverse a posted entry |
The six financial reports (all GET):
| Path | Report |
|---|---|
/reports/trial-balance | Trial balance |
/reports/profit-loss | Profit & loss (income statement) |
/reports/balance-sheet | Balance sheet |
/reports/vat-return | VAT return (ΦΠΑ) |
/reports/accounts-receivable | Accounts receivable ageing |
/reports/accounts-payable | Accounts payable ageing |
Gate: access_erp + erp_accounting (Business) + accounting.{view|update}. Reports are GET → accounting.view.
CRM — erp_crm
Contacts, leads/pipeline, activities, and segmentation. Controller: CrmApiController.
Path (under ec/v1) | Methods | Purpose |
|---|---|---|
/contacts | GET, POST | List / create contacts |
/contacts/stats | GET | Contact KPIs |
/contacts/tags | GET | Contact tag list |
/contacts/import-customers | POST | Import store customers as contacts |
/contacts/{id} | GET, PUT, DELETE | Read / update / delete a contact |
/contacts/{id}/activities | GET | Activities for a contact |
/contacts/{id}/leads | GET | Leads for a contact |
/leads | GET, POST | List / create leads |
/leads/stats | GET | Lead KPIs |
/leads/pipeline | GET | Pipeline view |
/leads/by-stage | GET | Leads grouped by stage |
/leads/{id} | GET, PUT, DELETE | Read / update / delete a lead |
/leads/{id}/move-stage | POST | Move a lead to another stage |
/leads/{id}/convert | POST | Convert a lead |
/activities | GET, POST | List / create activities |
/activities/stats | GET | Activity KPIs |
/activities/upcoming | GET | Upcoming activities |
/activities/overdue | GET | Overdue activities |
/activities/{id} | GET, PUT, DELETE | Read / update / delete an activity |
/activities/{id}/complete | POST | Mark an activity complete |
/crm/dashboard | GET | CRM dashboard aggregates |
/crm/segments | GET | Customer segments |
/crm/rfm | GET | RFM (recency/frequency/monetary) analysis |
Gate: access_erp + erp_crm (Business) + crm.{view|update}.
HR — erp_hr
Employees, departments, leave, time entries, documents — plus an employee self-service surface. Controller: HrApiController.
Manager-facing (require hr.{view|update} — in v1 only hr_manager and admin hold the hr module):
Path (under ec/v1) | Methods | Purpose |
|---|---|---|
/employees | GET, POST | List / create employees |
/employees/stats | GET | Employee KPIs |
/employees/{id} | GET, PUT/PATCH, DELETE | Read / update / delete an employee |
/employees/{id}/leave-balance | GET | Leave balance for an employee |
/employees/{id}/provision-ess | POST | Link a WP account for self-service |
/departments, /departments/{id} | GET, POST, PUT/PATCH, DELETE | Department CRUD |
/leave-types, /leave-types/{id} | GET, POST, PUT/PATCH, DELETE | Leave-type CRUD (the GET read is looser than the manage gate) |
/leave-requests, /leave-requests/{id} | GET, POST, … | Leave-request management |
/leave-requests/{id}/approve | POST | Approve a request |
/leave-requests/{id}/reject | POST | Reject a request |
/leave-requests/{id}/cancel | POST | Cancel a request |
/time-entries, /time-entries/{id} | GET, POST, … | Time-entry management |
/employee-documents, /employee-documents/{id} | GET, POST, … | Employee document management |
/hr/dashboard | GET | HR dashboard aggregates |
/hr/who-is-out | GET | Who is on leave |
HR self-service (ESS)
The /hr/me/* routes are the employee self-service surface. They use a different gate: canAccessEss — an active erp_hr license plus a WordPress account linked to an employee record (provisioned via /employees/{id}/provision-ess). Every ESS handler scopes strictly to the caller's own record, so a plain employee role (no manager modules) can still reach these.
Path (under ec/v1) | Methods | Purpose |
|---|---|---|
/hr/me | GET | The caller's own employee record |
/hr/me/leave-balance | GET | Own leave balance |
/hr/me/leave-requests | GET, POST | Own leave requests (view / submit) |
/hr/me/leave-requests/{id}/cancel | POST | Cancel own request |
/hr/me/time-entries | GET, POST | Own time entries |
/hr/me/documents | GET | Own documents |
Gate: manager routes → access_erp + erp_hr (Business) + hr.{view|update} (only hr_manager/admin in v1). ESS routes → erp_hr (Business) + linked employee record (canAccessEss).
ΕΡΓΑΝΗ (Ergani) — erp_hr, ERP-admin only
Submissions to the Greek labour authority (ΕΡΓΑΝΗ). Controller: ErganiApiController.
Path (under ec/v1) | Methods | Purpose |
|---|---|---|
/ergani/settings | GET, PUT | ΕΡΓΑΝΗ credentials + settings |
/ergani/test-connection | POST | Test the ΕΡΓΑΝΗ connection |
/ergani/submissions | GET | Submission history |
/ergani/submissions/{id}/retry | POST | Retry a failed submission |
Gate: requireErpAdmin('erp_hr') — the ERP admin role (or manage_options) plus erp_hr (Business). This is stricter than the hr.* module role: a plain hr_manager can manage employees but cannot touch ΕΡΓΑΝΗ (it writes WP-stored credentials, so it's admin-gated like ERP config).
ERP configuration & administration (admin-only)
Activation, credentials, ERP users/roles, data migration, inventory backfill, and onboarding. Controller: ErpConfigApiController. These routes are gated on the ERP admin role (or manage_options) via requireErpAdmin() / canManageErp — not the per-module RBAC matrix. Write routes that touch licensed features additionally require the license (canManageErpLicensed).
Path (under ec/v1) | Purpose |
|---|---|
/erp/status | ERP activation + config status |
/erp/activate, /erp/deactivate | Turn the ERP on/off |
/erp/test-connection, /erp/save-credentials | Separate-ERP-DB connection (see ERP architecture) |
/erp/users, /erp/users/{id}, /erp/users/{id}/remove, /erp/users/{id}/reactivate | ERP user management |
/erp/roles | The ERP role catalog |
/erp/migrate-data/preview, /erp/migrate-data/run, /erp/migrate-data/cleanup | Same-DB → separate-ERP-DB migration |
/erp/inventory/backfill/status, /erp/inventory/backfill/run | Backfill stock levels from existing products |
/erp/onboarding/checklist, /erp/onboarding/checklist/hide, /erp/onboarding/checklist/show | Setup checklist state |
/erp/products/apply-cost-source | Bulk cost-source assignment |
/erp/portal-settings | Branded ERP portal / standalone-login settings |
The OpenAPI spec (authoritative)
The plugin generates its own OpenAPI 3.0 spec covering the whole ec/v1 surface, including every ERP operation with exact parameters and schemas. Prefer it over any hand-written list (including this page).
Enable Settings → Developer → API Documentation, then fetch (admin-authenticated):
curl -u "admin:xxxx xxxx xxxx xxxx" \
https://your-site.com/wp-json/ec/v1/docs/openapi.json
The endpoint requires manage_options and the toggle. See the REST API Overview → OpenAPI spec for the in-admin viewer and playground, and for authentication (nonce vs. Application Passwords) and the common response/error envelopes — those apply identically to the ERP routes.
Where to go next
- ERP Architecture for Developers — how the entity → repository → service → controller layers connect, ERP-DB routing, and the commerce↔ERP integration-listener contract.
- REST API Overview — the full three-namespace map and shared conventions.