| Direction | Purpose |
| Deliverect -> OrderLemon | Menu, opening hours, busy mode, snooze, order status sync |
| OrderLemon -> Deliverect | paid orders and OrderLemon order status updates |
| Entry point | File | Purpose |
| `/v1/webhooks/deliverect.php` | `v1/webhooks/deliverect.php` | Main webhook router |
| `send_order_to_deliverect` | `v1/POST/send_order_to_deliverect.php` | Direct API function for sending an OL order to Deliverect |
Webhook handlers live in: `v1/webhooks/deliverect/`
| Handler | Purpose |
| `menu_update.php` | Menu, products, categories, modifiers, relations, images, availabilities |
| `opening_hours.php` | Deliverect opening hours -> OrderLemon |
| `order_status_update.php` | Deliverect order status -> OrderLemon |
| `busy_mode.php` | Busy delay / shop timing |
| `snooze_unsnooze.php` | Product snooze / unsnooze |
| `payment_completed.php` | Paid OL order -> Deliverect |
| `order_updated.php` | OL order status update -> Deliverect |
| `channel_registration.php` | Deliverect channel registration |
| `sync_opening_hours.php` | Manual OL opening-hours sync -> Deliverect via Postman |
Routing priority:
| Priority | Source | Notes |
| 1 | Query parameter for internal OL events | `?event=payment_completed`, `?event=order_updated`, `?event=sync_opening_hours` |
| 2 | Payload field | `type` or `event.type` |
| 3 | Payload shape detection | Used when no explicit type is present |
| 4 | Query parameter fallback | Accepted only if payload shape is compatible |
Supported query parameters:
?event=payment_completed
?event=order_updated
?event=sync_opening_hours
?event=menu_update
?event=order_status_update
?event=snooze_unsnooze
?event=busy_mode
?event=opening_hours
?event=channel_registration
Internal OrderLemon webhook URLs usually use `?event=`.
Example:
/v1/webhooks/deliverect.php?event=payment_completed
| Event | Direction | Purpose |
| `menu_update` | Deliverect -> OL | Sync products, categories, modifiers, relations, images, and availabilities |
| `opening_hours` | Deliverect -> OL | Sync weekly opening hours into OrderLemon |
| `order_status_update` | Deliverect -> OL | Update OrderLemon order status |
| `busy_mode` | Deliverect -> OL | Update temporary busy delay through `shops.min_timeframe` |
| `snooze_unsnooze` | Deliverect -> OL | Handle temporary product unavailability |
| `channel_registration` | Deliverect -> OL | Register channel link and return webhook URLs |
| `payment_completed` | OL -> Deliverect | Send a paid OrderLemon order to Deliverect |
| `order_updated` | OL -> Deliverect | Send OrderLemon order status changes to Deliverect |
| `sync_opening_hours` | OL -> Deliverect | Manual opening-hours sync, currently blocked by missing OL webhook event |
Reference: https://developers.deliverect.com/page/order-status
| Deliverect Status | Meaning |
| 0 | Unknown |
| 1 | Parsed |
| 2 | Received by POS |
| 3 | Sent to DMA |
| 4 | Before parsed |
| 5 | Receipt not found |
| 6 | Received by DMA |
| 7 | Printed by DMA |
| 10 | New (received at POS) |
| 20 | Accepted |
| 25 | Scheduled |
| 35 | Denied |
| 40 | Printed |
| 50 | Preparing |
| 60 | Prepared |
| 70 | Pickup_ready |
| 80 | In_delivery |
| 81 | Delivery created |
| 83 | En route to pickup |
| 84 | Almost at pickup |
| 85 | Arrived at pickup |
| 87 | En route to dropoff |
| 89 | Arrived at dropoff |
| 90 | Finalized / Delivered |
| 95 | Auto Finalized |
| 100 | Channel cancel |
| 110 | Canceled |
| 115 | Delivery canceled |
| 120 | Failed |
| 121 | POS failed |
| 122 | Retry failed |
| 124 | Parse failed |
Current implementation maps Deliverect final statuses to OrderLemon `done` (`status_id=3`), not `delivered` (`status_id=7`).
| Deliverect values | OrderLemon status_id | OrderLemon label |
| `0-7`, `10`, `20`, `25`, `new`, `parsed`, `received`, `accepted`, `scheduled` | 2 | ordered |
| `90`, `95`, `delivered`, `finalized`, `auto_finalized` | 3 | done |
| `40`, `50`, `60`, `printed`, `preparing`, `prepared` | 4 | packed |
| `70`, `ready`, `pickup_ready` | 5 | readyfordelivery |
| `80`, `81`, `83`, `84`, `85`, `87`, `89`, `in_delivery` | 6 | shipped |
| `35`, `100`, `110`, `115`, `denied`, `cancelled`, `canceled` | 8 | cancelled |
| `120`, `121`, `122`, `124`, `failed` | 9 | lost |
Notes:
Final Statuses: `done` (`3`) and `delivered` (`7`) are final OrderLemon statuses
Current Deliverect final mapping: Deliverect `90/95` maps to OL `done` (`3`)
Cash orders: Cash orders are marked `payment_status_label = paid` when finalized/delivered by Deliverect
Archive fallback: Status update searches `orders_active_{shop_id}` first, then `orders_archive_{shop_id}`
| OrderLemon status_id | OrderLemon label | Deliverect status |
| 1 | ordering | 10 |
| 2 | ordered | 20 |
| 3 | done | 90 |
| 4 | packed | 50 |
| 5 | readyfordelivery | 70 |
| 6 | shipped | 80 |
| 7 | delivered | 90 |
| 8 | cancelled | 110 |
| 9 | lost | 120 |
The `__G_` suffix means that an OrderLemon product row is group-scoped.
Example: XTRA-CHEESE__G_fb271dcb
Base reference: XTRA-CHEESE
The suffix identifies a specific modifier-group or upsell-group context.
This exists because the same Deliverect item can appear in multiple groups with different sync context. A scoped row allows OrderLemon to store group-specific assignment, pricing, relations, and availability without overwriting the base reference.
When comparing product identity, compare the base reference before `__G_`.
Scoped `__G_` rows are expected in modifier and upsell sync. They are not random duplicates and should not be skipped or deleted only because the base reference already exists.
| Data | Rule |
| Prices | Cents, for example `10.50` -> `1050` |
| Data/time | UTC ISO 8601, format `Y-m-d\TH:i:s\Z` |
| VAT/tax | Permyriad-style code, for example `9000 = 9%` |
| Opening hours | Deliverect uses `1..7` for Monday .. Sunday |
| OrderLemon weekdays | OrderLemon uses `0..6` for Monday .. Sunday |
Day Mapping:
| Deliverect | OrderLemon |
| 1 | 0 |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 5 | 4 |
| 6 | 5 |
| 7 | 6 |
| Event | Purpose |
| `payment_completed` | Send paid OrderLemon order to Deliverect |
| `order_updated` | Send OrderLemon status change to Deliverect |
`order_updated` only works for orders that have a Deliverect order ID mapping; otherwise returns `not_deliverect_order`.
Example URLs:
/v1/webhooks/deliverect.php?event=payment_completed
/v1/webhooks/deliverect.php?event=order_updated
Bearer auth hardening is currently not enforced until OrderLemon reliably sends the expected `Authorization: Bearer <secret>` header.
Paid orders are sent from OrderLemon to Deliverect
| Area | Behavior |
| Timezone | OL pickup and delivery times are stored in shop timezone and converted to UTC |
| Pickup fallback | Past or missing pickup time becomes ASAP fallback using shop `min_timeframe`, minimum 15 minutes |
| Items | Cart rows with `parent_id` become nested `subItems[]` |
| Discounts | Negative discount rows are not sent as regular items |
| Promotions | Promotions are aggregated into one `OrderLemon Promotion` discount |
| Delivery fee | Product reference `delivery_fee` becomes `deliveryCost` |
| Required fields | Payload includes `decimalDigits` and `orderIsAlreadyPaid` |
| Self-delivery | Delivery orders include `courier.deliveryBy = restaurant` |
| Success response | Deliverect can return plain `OK`, not always JSON |
Delivery courier payload:
{
"courier": {
"deliveryBy": "restaurant"
}
}The microservice:
| Step | Behavior |
| 1 | Resolves shop from `channelLinkId` / `locationId` |
| 2 | Uses `channelOrderId` as the OrderLemon order ID |
| 3 | Validates Deliverect status |
| 4 | Maps Deliverect status to OrderLemon `status_id` |
| 5 | Updates `orders_active_{shop_id}` or `orders_archive_{shop_id}` |
| 6 | Creates an OrderLemon order event to trigger downstream flow |
| 7 | Stores last Deliverect-set status to prevent echo loops with `order_updated` |
OrderLemon cart prices are gross VAT-inclusive.
Do not send `items[].tax`. It is not a valid Deliverect field for this integration.
VAT is sent at order level:
| Field | Behavior |
| `taxes[]` | Aggregated per VAT rate from cart item gross prices |
| `deliveryCostTax` | Always `0`; delivery fee VAT is not calculated separately |
| `serviceChargeTax` | Always `0`; because OL has no service charge concept |
`menu_update` synchronizes:
| Entity | Behavior |
| Products | Created or updated in `products_{shop_id}` |
| Categories | Created or updated as OL categories |
| Modifiers | Stored as category-level modifiers |
| Relations | Product/modifier/category relations are synchronized |
| Images | Category and product images are downloaded and uploaded to OL API |
| Availabilities | Category/menu availability becomes OL `allowed_dow` |
Reference resolution:
| Context | Reference fallback |
| Menu sync products | `referenceId -> plu -> productId -> payload key` (context-dependent) |
| General helper | `plu -> referenceId -> productId -> deliverect_id -> _id -> id -> fallback` |
| Catalog entity identity | Stable IDs are preferred before PLU/reference aliases |
Reference resolution order is context-dependent. Menu sync prefers `referenceId` first, while the general helper prefers `plu` first.
Important menu sync rules:
| Rule | Details |
| `snoozedProducts` | Must not change `enabled` state during `menu_update` |
| Snooze handling | Availability is handled by `snooze_unsnooze` |
| Modifiers | Modifier model is category-level, not product-level |
| Combo roots | Combo/bundle root products can be disabled while variants remain sellable |
| Group-scoped rows | `__G_` rows can have group-specific price, assignment, and availability |
| Duplicate guard | Must not skip rows that have assigned modifier-group context |
| `allowed_dow` priority | Category availabilities > menu-level availabilities > menu schedule > empty |
Deliverect data is stored in the `metadata` JSON column on per-shop tables under the `deliverect` namespace. Other namespaces in metadata are never touched (read-modify-write).
- `categories_{shop_id}` → `external_id` (Deliverect category `_id`)
- `products_{shop_id}` → `product_type` (Deliverect productType), `combo_variants` (variant references for combo shells)
- `orders_active/archive_{shop_id}` → `order_id` (Deliverect order `_id`, set after send), `last_status` (last OL status set by `order_status_update`, used for loop prevention)
OAuth2 token is account-level and cached in `shop_settings` as `deliverect.oauth.access_token` and `deliverect.oauth.expires_at` under a shop_id.
Busy mode updates temporary shop delay through `shops.min_timeframe`
busy statuses:
| Payload status | Action |
| `BUSY` | Add busy delay |
| `PAUSED` | Add busy delay |
| `CLOSED` | Add busy delay |
| `OFFLINE` | Add busy delay |
| `ONLINE` | Revert previously added busy delay |
| `ACTIVE` | Revert previously added busy delay |
| `OPEN` | Revert previously added busy delay |
| `AVAILABLE` | Revert previously added busy delay |
| `UNPAUSED` | Revert previously added busy delay |
| `OFF` | Revert previously added busy delay |
Rules:
| Rule | Details |
| Delay required | Busy-on events need a positive `delay` |
| Existing delay | Existing busy delay is deducted before applying a new one |
| Max value | `min_timeframe` is capped at `99` |
| Storage | Added delay is stored in `shop_settings` |
| Locking | Uses `SELECT ... FOR UPDATE` during update |
Opening hours sync goes from Deliverect to OrderLemon.
Rules:
| Rule | Details |
| Direction | Deliverect -> OrderLemon |
| Missing weekday | Treated as closed |
| Overwrite | Weekly Deliverect availability overwrites existing OL weekly schedule |
| Max slots | Max 2 slots per day |
| Slot update order | Existing slots update by `id ASC` |
| Source of truth | `availabilities` is the source of truth |
| Holiday hours | Not supported because Channel API payload has no holiday/special-hours context |
Manual OL -> Deliverect opening hours sync exists as `sync_opening_hours`, but automatic sync is blocked because OrderLemon has no webhook event for opening-hour changes.
Channel registration stores Deliverect channel link/location mapping and returns event-specific webhook URLs
Returned URLs include:
| URL key | Event |
| `statusUpdateURL` | `order_status_update` |
| `menuUpdateURL` | `menu_update` |
| `snoozeUnsnoozeURL` | `snooze_unsnooze` |
| `busyModeURL` | `busy_mode` |
| File | Responsibility |
| v1/general/deliverect_functions.php | Deliverect Channel API communication, OAuth2, order sending |
| v1/general/deliverect_catalog_functions.php | Catalog and menu sync helpers |
| v1/general/deliverect_sync_functions.php | Sync utilities, mapping, relation logic |
| v1/general/deliverect_order_functions.php | Order transformation and status mapping |
| v1/general/ms_functions.php | Main microservice logic |
| v1/webhooks/deliverect.php | Main webhook router |
| v1/webhooks/deliverect/*.php | Event-specific webhook handlers |
| v1/POST/send_order_to_deliverect.php | Direct order send endpoint |
| Environment | URL |
| DEV | https://deliverect.ms.orderlemon.dev/v1/ |
| PROD | https://deliverect.ms.orderlemon.com/v1/ |
| Webhook | https://deliverect.ms.orderlemon.{dev|com}/v1/webhooks/deliverect |
Automatic OrderLemon to Deliverect opening-hours sync is blocked by missing OrderLemon webhook events.
| Constraint | Status |
| Holiday hours | Not supported by Deliverect Channel API weekly availability payload |
| OL -> Deliverect automatic opening-hours sync | Blocked by missing OrderLemon webhook event |