Create Order

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…

Generic Picking

Generic Picking lets a third-party picking partner play the same role Deliverect's own
Quest picker plays: it receives retail orders to pick, reports picking progress, requests
substitutes, and finalises the order. It is a push + callback integration, not a
POS↔channel bridge.

TL;DR — A retail channel (Deliveroo, Uber Eats, …) sends an order to Deliverect.
Deliverect pushes a filtered copy of that order to the partner's orderWebhookUrl. The
partner then calls back into Deliverect's /picking/order/... endpoints (OAuth genericPicking
scope) to drive the picking lifecycle. The partner never creates or fetches orders.


Roles (read this first)

TermWhat it is
ChannelThe marketplace/storefront where the consumer ordered (Deliveroo, Uber Eats, Just Eat/Flyt, Glovo, Wolt, DiDi Food, Delivery Hero, DoorDash, PIK, or a Generic retail channel). Orders originate here.
DeliverectThe middleware. Ingests the channel order, creates a retail order, routes it to a picker.
PickerThe app doing the physical picking. Quest is Deliverect's own picker; Generic Picking is the same role exposed to an external partner.
POSThe store's point-of-sale/inventory system. A separate integration (genericPOS scope). A partner may be both a POS and a picker, but the roles/scopes are distinct — the picking API is not a POS feature.

End-to-end flow

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'fontSize': '24px'}}}%%
sequenceDiagram
participant C as Retail Channel
participant D as Deliverect
participant P as Picking Partner

C->>D: New retail order
D->>P: POST {orderWebhookUrl}/picking/order  (filtered order, HMAC)

P->>D: POST /picking/order/{id}/start
P->>D: POST /picking/order/{id}/item/{itemId}/suggestSubstitute
D-->>P: 200 [substitute candidates]  (synchronous — pulled, not pushed)
P->>D: POST /picking/order/{id}/item/{itemId}/pick
P->>D: POST /picking/order/{id}/updateOrderItems  (ADJUST / REPLACE / REMOVE / PICK)
Note over D: amendments/substitutions forwarded to the CHANNEL
P->>D: POST /picking/order/{id}/done

D->>P: POST {orderWebhookUrl}/picking/order/{id}/update  (lifecycle events, e.g. CUSTOMER_APPROVAL_SUBSTITUTION)

Two directions, don't conflate them:

  • Outbound (Deliverect → partner) — controlled by POS settings. New order + every lifecycle
    event are POSTed to the single orderWebhookUrl base.
  • Inbound (partner → Deliverect) — the /picking/order/... endpoints, gated by the OAuth
    genericPicking scope (+ HMAC). Not controlled by POS settings.

Configuration (POS settings)

Only two settings are wired. Set them on the location — Deliverect propagates location POS
settings to that location's channel links, so every channel at the location routes to the same picker.

SettingWired?Meaning
isGenericPickingActiveRoute this link's orders to the generic partner instead of Quest.
orderWebhookUrlPartner base URL. Deliverect appends /picking/order (new order) and the lifecycle sub-paths. Give the base only — no trailing slash, no /picking/order.

The order push is fire-and-forget with no retry (_sendToGenericPicking logs and swallows
errors). If the partner endpoint is down/slow (10s timeout) the order silently doesn't arrive and
Deliverect does not error — check logs for [GenericPicking].


Inbound endpoints (partner → Deliverect)

Base: https://api.deliverect.com · Auth: OAuth2 client-credentials, scope value genericPicking
(the camelCase string — GENERIC_PICKING is only the internal enum member name) · POSTs are additionally
HMAC-signed (X-Deliverect-Hmac-Sha256).

#Method & pathPurpose
1POST /picking/order/{id}/startStart picking
2POST /picking/order/{id}/item/{itemId}/pickMark an item picked
3POST /picking/order/{id}/item/{itemId}/suggestSubstituteGet substitute candidates (synchronous response)
4POST /picking/order/{id}/updateOrderItemsAmend items (ADJUST / REPLACE / REMOVE / PICK)
5POST /picking/order/{id}/rejectReject the order
6POST /picking/order/{id}/doneFinish picking
7POST /picking/order/{id}/couriersUpdate required courier count

There is intentionally no POST /picking/order (create) and no GET /picking/order/{id}
(fetch) — orders are pushed, not created or pulled by the partner, matching Quest's contract. A pull
endpoint can be added later if a genuine use case appears (e.g. reconciliation).

This inbound set mirrors Quest's own callbacks (routes/retail/routes.py) and shares the same
processor functions (Middleware/RetailButler/pickerRoutesUtils.py), so behaviour is identical to
Quest — the only difference is the partner receives a filtered order payload
(filterOrdersGenericPicking).


Substitutes: pulled, not pushed

  • The partner pulls candidates by calling suggestSubstitute; Deliverect returns them
    synchronously in the HTTP response. Depending on the item's unavailable-action, candidates come
    from the channel (_getCandidatesFromChannelAPI) or from Deliverect's catalog
    (_getCandidatesFromCatalog, only for channels with supportsSubstitutionStrategy — currently
    Uber Eats and Flyt/Just Eat).
  • Applying a substitution/amendment (updateOrderItems REPLACE) is forwarded to the channel
    via the channel settings retailSubstitutionsEndpointURL / retailAmendmentsWebhookURL
    (that's the Channels/Generic layer, not a picker setting).
  • Substitution results reach the partner via the outbound update lifecycle event
    (eventType=CUSTOMER_APPROVAL_SUBSTITUTION) to orderWebhookUrl. There is no separate
    "substitutes webhook".

HMAC

Outbound pushes are signed: X-Deliverect-Hmac-Sha256 = HMAC_SHA256(key=secret, msg=<raw JSON body>)
(hex). The secret is the configured GENERIC_PICKING extension key; when it isn't configured
(typical local/staging) it falls back to the location id, which is present as the location
field in the payload — so a receiver can self-verify with that value.



Notes / gotchas

  • Config scope: set the active flag and URL on the location. Deliverect propagates location POS
    settings to the location's channel links (ChannelButler on link creation +
    updateAndPropagateLocationPOSSettings), so both scopes resolve to the same values and every channel
    at the location routes to the one picker.
  • No retry on the outbound push (see above).
  • Historically there were three extra POS settings (genericPickingApiKey, orderUpdatesUrl,
    getSubstitutesUrl) that were never consumed; they have been removed. Access is controlled by the
    OAuth scope, updates and substitutes both use the mechanisms described above.

Response
200
Language
LoadingLoading…
Response
Click Try It! to start a request and see the response here!