Appearance
Uber Eats Integration
Overview
The Uber Eats integration connects an Upvendo location to an Uber Eats store so that your menu is pushed out to Uber Eats and incoming Uber Eats orders are received automatically into Upvendo.
Key Purpose: Push your menu to Uber Eats and receive Uber Eats orders inside Upvendo.
Purpose
This page lets you connect a single Upvendo location to an Uber Eats store. You authorize Upvendo through Uber's OAuth flow, pick the store to link, then push selected Upvendo menus to Uber Eats and receive Uber Eats orders automatically via webhooks. Payments are handled entirely by Uber Eats.
Key Concepts
- OAuth Authorization (per merchant): Linking a store requires an Uber OAuth authorization code flow with the
eats.pos_provisioningscope. The merchant logs in to Uber and authorizes Upvendo; the resulting user access token and refresh token are stored on the integration record and used to read the store list and to link/unlink the store. (A separate client credentials token, shared across the app, is used for order and menu API calls — see the integration setup doc.) - Store Linking (POS provisioning): After OAuth, you select a store from your Uber account and link it. Upvendo calls Uber's POS provisioning endpoint (
pos_data) to register Upvendo as the order manager (is_order_manager: true), setrequire_manual_acceptance: false, and enable order-release, scheduled-order and delivery-status webhooks. (Verified:app/Services/BackOffice/UberEatsService.phplines 640-663.) - Location-Scoped: The integration is scoped to one Upvendo location. Each location links its own Uber Eats store. The OAuth tokens, however, are stored on the per-location integration record (there is no shared vendor-level credential store).
- Webhook-Driven Orders: Uber Eats sends webhooks to Upvendo.
UberEatsWebhookServiceresolves the matching integration in three steps — first by integrator/webhook identifier, then by Upvendo location ID, then by Uber store ID — and dispatches a background job per event type. New orders are auto-accepted once the local transaction is safely stored. (Verified:app/Services/ThirdParty/UberEatsWebhookService.phplines 125-185.) - Menu Sync (push-out): The
syncMenuaction transforms selected Upvendo menus into Uber Eats' format (categories, items, modifier groups with quantity constraints, prices, tax rates, allergen classifications, and availability schedules) and pushes them with a single PUT request to Uber's Menu API. You trigger it from the integration page — and once menus have been synced, editing one of those menus in Upvendo re-pushes the whole set automatically in the background. (Verified:app/Services/BackOffice/UberEatsService.phplines 217-286 andtransformMenuForUberEatslines 292-507; auto re-sync inapp/Listeners/SyncMenuWithUberEats.phplines 29-42, fired fromapp/Services/BackOffice/MenuService.phpline 127.) - Sync Status: Each integration record tracks a sync status used during menu sync. The stored status enum (
ThirdPartySyncStatus) has three values:standby,syncing, anderror, each with a timestamp and message. (Verified:app/Enums/ThirdPartySyncStatus.php.) - Connection Status: Separately from sync status, the status endpoint reports a connection state derived from OAuth and store-selection state:
not_connected,pending_store_selection,connected, orprovisioning_failed. If a storedsettings.connection_statusexists it is reported instead ofprovisioning_failed— for exampledeprovisionedafter Uber deprovisions the store. (Verified:app/RawModels/UberEatsIntegration.phplines 97-111;deprovisionedwritten inapp/Services/ThirdParty/UberEatsWebhookService.phplines 81-93.) - Capabilities: Which Uber features a location can use depends on the Uber scopes approved for it. The status response returns a
capabilitiesmap (store_read,store_write,menu,orders_read,order_actions,promotions,reports,merchant_delivery), eachavailabletrue/false with areasonwhen unavailable. Calls into an unavailable capability are rejected. (Verified:app/RawModels/UberEatsIntegration.phplines 157-185; enforcement inapp/Services/UberEats/UberEatsMarketplaceService.phplines 266-275.)
Actions
Connect Uber Eats Account
Click Connect to Uber Eats. Upvendo creates an integration record and returns an Uber OAuth URL (scope eats.pos_provisioning); the browser is redirected to it. After you authorize on Uber, Uber redirects back to Upvendo's callback, which exchanges the authorization code for an access token and refresh token and stores them on the integration record.
Select and Link a Store
After OAuth completes, pick a store from the Select Store dropdown (populated from your Uber account) and confirm. Upvendo saves the store ID while leaving the integration inactive, calls Uber's POS provisioning endpoint to link the store and enable webhooks, and only then marks the integration active with connection_status: connected. If provisioning fails, the store stays saved but the integration stays inactive, so you can retry the selection without reconnecting. (Verified: app/Services/BackOffice/UberEatsService.php lines 166-196.)
Sync Menu to Uber Eats
Select one or more Upvendo menus and push them to Uber Eats. The sync sends categories, items, modifier groups (with quantity constraints), prices, tax rates, allergen classifications, and availability schedules, and replaces the menu on Uber Eats. After the first sync, editing any of those menus in Upvendo re-pushes the set automatically in the background — so the button is mainly for the first push, for changing which menus are synced, and for forcing a re-push.
Disconnect Uber Eats
Disconnecting calls Uber's pos_data DELETE endpoint to unlink the store (best-effort — failures are logged and ignored) and then deletes the integration record. You will stop receiving orders for that location. (Verified: app/Services/BackOffice/UberEatsService.php lines 671-699.)
The disconnect endpoint (
DELETE /back-office/uber-eats/{locationId}) exists on the backend, but the Uber Eats page in the back office does not currently render a disconnect button — the page shows only Connect, Select Store, and Sync Menu. (Verified:upvendo-backoffice/src/views/uber-eats/index.vuelines 1-201; thedisableUberEatsaction insrc/store/modules/uberEats.tslines 95-102 has no caller.)
Location
- Backoffice Route:
/uber-eats(Uber Eats integration page). (Verified: file-based route fromupvendo-backoffice/src/pages/uber-eats/index.vue.) - Backend Controller:
app/Http/Controllers/Api/BackOffice/UberEatsIntegrationController.php - Backend Services:
app/Services/BackOffice/UberEatsService.php(connect / store link / menu sync),app/Services/UberEats/UberEatsMarketplaceService.php(settings, store, remote menu, orders, promotions, reports),app/Services/UberEats/UberEatsTransactionService.php(incoming-order mapping) - Vue Component:
src/views/uber-eats/index.vue
Fields
There are no credential-entry fields. Uber OAuth credentials are platform-level (configured by the Upvendo team as environment variables), so the merchant never enters a client ID, client secret, or store ID by hand. On the Uber Eats page itself, the back office renders only a store dropdown (after OAuth) and a menu multi-select with a Sync Menu button — no enabled, auto-accept, availability-sync, prep-time, or sandbox control is drawn on that page. (Verified: upvendo-backoffice/src/views/uber-eats/index.vue lines 1-201.)
Behind that page the backend exposes a much wider marketplace API for the same location — integration settings (PUT .../settings), store details/status/prep-time/fulfillment, remote-menu read and per-item update, order list/detail/actions, promotions, and reports. These are live production endpoints; they are simply not wired into the Uber Eats page yet, so treat them as API capabilities rather than on-screen controls. (Verified: routes/api/backoffice/uber-eats.php lines 7-44; the back-office store module calls only status, connect, store-list, select-store, sync-menu, and disconnect — upvendo-backoffice/src/store/modules/uberEats.ts lines 47-102.)
Order Send Timing (integration settings)
PUT /back-office/uber-eats/{locationId}/settings stores two merchant-facing settings that control when an accepted Uber order is pushed to the in-house POS:
| Field ID | Type | Values | Default |
|---|---|---|---|
pos_send_timing | string | location_default, immediate, scheduled | location_default |
pos_lead_minutes | integer | 0-1440 (used only with scheduled) | 0 |
location_default follows the location's online-ordering rules; immediate sends as soon as the order is accepted; scheduled sends pos_lead_minutes before the order's pickup/delivery time. Both fields are required on that request. (Verified: app/Http/Requests/BackOffice/UberEatsIntegration/UpdateUberEatsSettingsRequest.php lines 13-16; defaults in app/RawModels/UberEatsIntegration.php lines 114-117; behaviour in app/Services/UberEats/UberEatsTransactionService.php lines 685-739.)
Store
| Property | Value |
|---|---|
| Field ID | store_id |
| Label | Select Store |
| Type | Dropdown (populated from the Uber store list after OAuth) |
| Required | Yes (to link a store) |
Description: The Uber Eats store to link to this location. Chosen from the dropdown of stores fetched from your authorized Uber account, not typed in.
Menus to Sync
| Property | Value |
|---|---|
| Field ID | menu_ids |
| Label | Select Menus |
| Type | Multi-select (Upvendo menu IDs) |
| Required | Yes (for Sync Menu) |
Description: The Upvendo menus to push to Uber Eats when you click Sync Menu. Multiple menus can be selected and are combined into one payload.
Business Logic
Order Flow
Customer orders on Uber Eats
|
v
New-order webhook received (orders.notification / orders.release / ...)
|
v
ProcessUberEatsOrderNotificationJob (queued)
|
v
Full order fetched from Uber API
|
v
Transaction + KDS items created in Upvendo
|
v
Order auto-accepted on Uber (only after the local order is stored)
|
v
sent_at calculated -> order forwarded to in-house POS (if configured)
|
v
Kitchen prepares -> driver picks up(Verified: app/Jobs/UberEats/ProcessUberEatsOrderNotificationJob.php lines 96-133.)
Menu Sync
Select menus in Upvendo
|
v
Click Sync Menu -- or edit an already-synced menu (auto re-sync)
|
v
Convert to Uber Eats format (categories, items, modifier groups, schedules)
|
v
PUT to Uber Eats Menu API
|
v
sync_status updated on the integration recordOrder Status
Uber Eats orders are received already paid (Uber handles payment). On a new order, the transaction is stored with order_status: Queued and status: Complete, and Upvendo calls Uber's accept endpoint to auto-accept. Cancellations from Uber set the transaction order_status to Cancelled and mark its items done. Upvendo does not push a custom Confirmed/Preparing/Ready status sequence back to Uber Eats. (Verified: app/Services/UberEats/UberEatsTransactionService.php lines 114-165; app/Jobs/UberEats/ProcessUberEatsCancelNotificationJob.php.)
If an incoming order cannot be mapped locally — an unknown item, modifier group or modifier, a missing order ID, an empty cart, an invalid total, or free-text item instructions — Upvendo denies the order on Uber with reason type ITEM_ISSUE and code INVALID_ORDER instead of accepting it, and logs the reason. No transaction is created. (Verified: app/Jobs/UberEats/ProcessUberEatsOrderNotificationJob.php lines 101-117; validation in app/Services/UberEats/UberEatsTransactionService.php lines 653-677 and lines 318-441.)
Business Rules
- Connecting starts an OAuth authorization-code flow (scope
eats.pos_provisioning); the store is linked only in the separate Select Store step, which calls Uber's POS provisioning (pos_data) endpoint withis_order_manager: true,require_manual_acceptance: false,integrator_brand_id: upvendo_brand,integrator_store_idset to the Upvendo integration record ID,merchant_store_id: upvendo_{locationId}, and order-release, scheduled-order and delivery-status webhooks all enabled (webhooks_version: 1.0). (Verified:app/Services/BackOffice/UberEatsService.phplines 631-669.) - Disconnecting calls Uber's
pos_dataDELETE endpoint to unlink the store (best-effort) and then deletes the local integration record, so the marketplace stops sending orders. - Menu sync converts prices to the smallest currency unit and applies each item's/modifier group's delivery tax rate. Items also carry their Upvendo allergens as
dish_info.classifications. The payload setsdisable_item_instructions: true(customers cannot add free-text item instructions). (Verified:app/Services/BackOffice/UberEatsService.phplines 443-506.) - Webhook events are matched to an integration in three steps, in order: (1) the integrator/webhook identifier —
meta.integrator_store_id, a top-levelintegrator_store_id, or a partner identifier of typeINTEGRATOR_STORE_ID— matched againstsettings.webhook_identifierand then against the integration_id; (2) the Upvendo location, frommeta.location_idor frommerchant_store_id/partner_store_idwith theupvendo_prefix stripped; (3) the Uber store ID (meta.user_id,meta.store_idor a top-levelstore_id) matched againstcredentials.store_id, preferring an active integration. Payloads with none of these identifiers, and payloads that match no integration, are logged and dropped. (Verified:app/Services/ThirdParty/UberEatsWebhookService.phplines 125-253.) - Webhook delivery is acknowledged immediately and processed on the
webhooksqueue byProcessUberEatsWebhookJob(4 tries, backoff 1/5/30/120s), which de-duplicates on the Uber event ID (event_idorwebhook_meta.webhook_msg_uuid) so a replayed webhook is not processed twice. Order events are then dispatched toProcessUberEatsOrderNotificationJob,ProcessUberEatsScheduledNotificationJob, orProcessUberEatsCancelNotificationJob, each with a unique lock keyed on the Uber order ID (meta.resource_id). (Verified:app/Jobs/UberEats/ProcessUberEatsWebhookJob.phplines 13-58.) - New orders are auto-accepted for the merchant:
require_manual_acceptance: falseat provisioning, and the order job calls Uber's accept endpoint once the transaction and KDS rows are stored. Orders that fail local validation are auto-denied instead. There is no manual-accept toggle on the Uber Eats page, but the backend does expose per-order actions (accept,deny,cancel,ready,ready-time,adjust-price,validate-fulfillment,resolve-fulfillment,replacement-recommendations,courier-count,merchant-delivery-status) viaPOST /back-office/uber-eats/{locationId}/orders/{orderId}/{action}. (Verified:routes/api/backoffice/uber-eats.phplines 32-33.) - If Uber sends a
store.deprovisionedevent, Upvendo sets the integrationis_active = falseandsettings.connection_status = deprovisioned. The record is kept, so reselecting the store restores it. (Verified:app/Services/ThirdParty/UberEatsWebhookService.phplines 81-93.) - Store, menu, order, promotion and report calls are gated by the location's approved Uber scopes and return a 409 when the scope is missing. (Verified:
app/Services/UberEats/UberEatsMarketplaceService.phplines 266-275.) - Payment is external: Uber Eats handles all customer payments and you receive payouts from Uber directly. No payment profile is needed in Upvendo.
FAQs
- Do I need to enter Uber Eats API credentials? No. Upvendo uses platform-level OAuth credentials configured server-side. You authorize via Uber's login flow; Upvendo stores your user access and refresh tokens on the integration. You never type a client ID, secret, or store ID.
- Can I set different prices for Uber Eats? Yes — items support an Uber Eats channel price. Incoming orders are priced using the Uber Eats channel price (
getPrice('Uber Eats')) of the matched Upvendo item, and menu sync pushes item prices to Uber. Set the Uber Eats price on items if it should differ from the default. - What happens when an order is cancelled on Uber Eats? Uber sends an
orders.cancelwebhook, which triggers a background job that sets the transaction's order status to Cancelled and marks its items done. (Nothing is deleted.) - How are modifier quantity limits handled? Modifier group settings (mandatory, allow-duplicates, max-selected) are translated into Uber Eats'
quantity_infoformat withmin_permitted/max_permittedvalues, using per-modifieroverrides(contextMODIFIER_GROUP) and group-level quantity where applicable. - Can I connect multiple stores? Each Upvendo location links one Uber Eats store. The store list is fetched with pagination, and you can link different locations to different stores; each location runs its own OAuth + store-selection.
- My Uber Eats integration turned itself off — why? Uber can deprovision a store (for example when the store is removed or the POS link is revoked from Uber's side). Upvendo receives a
store.deprovisionedwebhook and deactivates the integration, settingconnection_statustodeprovisioned. Nothing is deleted — reselect the store on the Uber Eats page to re-provision and reactivate. (Verified:app/Services/ThirdParty/UberEatsWebhookService.phplines 81-93.) - Why did an Uber order get rejected automatically? Upvendo denies an incoming order (reason type
ITEM_ISSUE, codeINVALID_ORDER) when it cannot be mapped locally — most often an item, modifier group or modifier on the Uber menu that no longer exists in Upvendo, or an order carrying free-text item instructions. Re-sync the menu so Uber's copy matches Upvendo. (Verified:app/Jobs/UberEats/ProcessUberEatsOrderNotificationJob.phplines 101-117.) - Do menu edits push to Uber Eats automatically? Only for menus you have already synced. Editing a menu whose ID is in the integration's
menu_idsre-pushes the whole synced set in the background; a menu that has never been synced still needs the Sync Menu button. (Verified:app/Listeners/SyncMenuWithUberEats.phplines 29-42.) - When is an Uber order sent to my POS? By default it follows the location's online-ordering rules. The integration settings can override this with
pos_send_timing=immediate(send on acceptance) orscheduledwithpos_lead_minutes(send that many minutes before the pickup/delivery time). (Verified:app/Services/UberEats/UberEatsTransactionService.phplines 685-739.)
Troubleshooting
Problem: Orders not coming through
Causes:
- Integration not connected / store not selected (connection status is
not_connectedorpending_store_selection) - Store selected but provisioning never completed (
provisioning_failed, integration still inactive) - OAuth tokens expired or store not linked on Uber's side
- Uber deprovisioned the store (
connection_status: deprovisioned, integration inactive) - Restaurant offline in Uber Eats Manager
Solutions:
- Connect and complete store selection (provisioning enables webhooks)
- Re-select the store — the record stays saved and inactive on a failed provision, so a retry is safe
- Disconnect and reconnect to refresh OAuth and re-link the store
- Re-select the store to re-provision after a deprovision
- Check the Uber Eats dashboard and the Activity Log for errors
Problem: Integration switched itself off
Causes:
- Uber sent a
store.deprovisionedwebhook for the linked store (store removed, or the POS link revoked in Uber Eats Manager) - Store selection was made but Uber's provisioning call failed, so the record was never activated
Solutions:
- Check the connection status:
deprovisionedmeans Uber ended the link,provisioning_failedmeans Upvendo never completed it - Re-select the store on the Uber Eats page to re-provision and reactivate — the integration record and its synced
menu_idsare preserved either way - If the store no longer exists in Uber Eats Manager, recreate/relist it on Uber first
(Verified: app/Services/ThirdParty/UberEatsWebhookService.php lines 81-93; app/RawModels/UberEatsIntegration.php lines 100-111.)
Problem: An Uber Eats action returns "This Uber Eats capability is unavailable"
Causes:
- The location's approved Uber scopes do not cover the feature (
reason: scope_not_approved) - Merchant-managed delivery is not switched on for the location (
reason: merchant_delivery_not_enabled) - The capability is not present in the status response at all (
reason: capability_unknown)
Solutions:
- Check the
capabilitiesmap in the integration status response to see which features are available - Promotions (
eats.store.promotions) and reports (eats.report) are outside the default scope set, so they need the extra scopes approved on the Uber app before they will work - Merchant-delivery status updates additionally require
settings.merchant_delivery_enabled
(Verified: app/Services/UberEats/UberEatsMarketplaceService.php lines 266-275; app/RawModels/UberEatsIntegration.php lines 157-185; default scopes in config/services.php lines 192-195.)
Problem: Menu sync fails
Causes:
- No store linked / invalid store
- Items missing names, prices, or tax rates
- Invalid modifier quantity constraints rejected by Uber
Solutions:
- Re-select and link the store
- Fix item data and re-sync
- Review modifier group settings, then re-sync
Examples
Connect and sync (typical flow)
1. Open the Uber Eats page (`/uber-eats`, listed under the Online channels in the sidebar) and click "Connect to Uber Eats"
2. Authorize Upvendo on Uber (OAuth)
3. Back in Upvendo, pick your store from the dropdown and confirm
4. Select the menus to push and click "Sync Menu"Select Store request body
store_name is optional; when supplied it is stored as settings.selected_store_name and shown as the connected store. (Verified: app/Http/Requests/BackOffice/UberEatsIntegration/SelectUberEatsStoreRequest.php lines 17-21.)
json
{
"store_id": "store-uuid-1",
"store_name": "Main Street Kitchen"
}Sync Menu request body
json
{
"menu_ids": ["menu-id-1", "menu-id-2"]
}Update settings request body
json
{
"pos_send_timing": "scheduled",
"pos_lead_minutes": 20
}