Event: Quote Request

delivery_provider.quote_request — pricing a delivery while the customer is still at checkout.

Event: Quote Request

event_type: delivery_provider.quote_request

Sent while a customer is at checkout and the channel needs a delivery price. It is a question, not a commitment: most quotes are never dispatched, and an unused quote costs nothing.

Request

{
  "event_id": "9c2f4b71-0e3a-4d55-8f21-6b7c8d9e0a1b",
  "event_type": "delivery_provider.quote_request",
  "event_timestamp": 1785062400,
  "user_id": 12,
  "data": {
    "brand_id": null,
    "brand_guid": null,
    "external_store_id": "PZ-0031",
    "provider_store_id": "STORE-8871",
    "pickup": {
      "name": "Pizza Place Hillcrest",
      "address": "10 Old Main Road, Hillcrest, 3610",
      "latitude": "-29.778100",
      "longitude": "30.762200",
      "phone_number": "+27317650000",
      "ready_at": null,
      "deadline_at": null
    },
    "dropoff": {
      "name": "Jane Roe",
      "address": "25 Dovehouse Road, Hillcrest 3650",
      "latitude": -29.7779,
      "longitude": 30.7889,
      "phone_number": "+27825550100",
      "ready_at": null,
      "deadline_at": null
    },
    "manifest": {
      "total_value_cents": 10590
    }
  }
}

data

FieldTypeNotes
brand_idnullInternal to YUMBI Gateway. Always null for you.
brand_guidnullInternal to YUMBI Gateway. Always null for you.
external_store_idstringYUMBI Gateway's store identifier.
provider_store_idstring | nullYour identifier for the store. Prefer this.
pickupobjectWhere the food is collected. See below.
dropoffobjectWhere it goes. See below.
manifest.total_value_centsinteger | nullGross value of the order in cents, as the channel reported it at checkout.

pickup and dropoff

Both use the same shape.

FieldTypeNotes
namestring | nullStore name on pickup; customer name on dropoff.
addressstring | nullOne line. The store's address is composed from its address lines, city and postal code.
latitudenumber | string | nullDecimal degrees.
longitudenumber | string | nullDecimal degrees.
phone_numberstring | nullNormalised to E.164 before it reaches you.
ready_atISO 8601 | nullWhen the order will be ready. Usually null at quote time.
deadline_atISO 8601 | nullLatest acceptable time. Usually null at quote time.
📘

Coordinates may be numbers or strings

Pickup coordinates come from YUMBI Gateway's own store record and serialise as decimal strings ("-29.778100"). Dropoff coordinates come from the channel's request and are usually JSON numbers. Parse both defensively — do not assume either type.

Pickup details are taken from YUMBI Gateway's store record, with the channel's copy as a fallback. Both sides hold the same store and either can be stale, but the store record is the one dispatch will also build its pickup block from, so a quote and its dispatch always describe the same pickup point.

Phone numbers are normalised to E.164 before we send them, against the brand's country. If a number cannot be normalised it is sent as-is rather than dropped — your own validation is the honest failure at that point.

Response

Return 200 with:

{
  "provider_quote_id": "prv_qt_abc123",
  "currency_type": "ZAR",
  "fee_in_cents": 4500,
  "pickup_duration_in_minutes": 5,
  "duration_to_reach_dropoff_in_minutes": 22,
  "dropoff_eta": "2026-08-07T14:32:00+02:00",
  "dropoff_deadline": "2026-08-07T14:50:00+02:00",
  "expiration": "2026-08-07T14:15:00+02:00"
}
FieldTypeNotes
provider_quote_idstring | nullYour reference for this price. Returned to you on the dispatch that honours it.
currency_typestring | nullISO 4217, e.g. ZAR.
fee_in_centsinteger | nullWhat you will charge for the delivery.
pickup_duration_in_minutesinteger | nullUntil a driver reaches the store.
duration_to_reach_dropoff_in_minutesinteger | nullFrom pickup to dropoff.
dropoff_etaISO 8601 | nullWhen the customer receives it.
dropoff_deadlineISO 8601 | nullLatest you will commit to.
expirationISO 8601 | nullWhen this price lapses.

Every field is optional and unknown fields are ignored, but a quote with no fee_in_cents gives the channel nothing to show the customer.

Quote reuse — why expiration matters

Checkout re-quotes constantly: a customer editing their basket or address can fire a dozen requests in a minute, and several can arrive within milliseconds of each other. YUMBI Gateway absorbs that so you do not have to.

Caching. A quote is reused, without calling you again, when the channel, store, provider and a content hash of the request all match. That hash covers the pickup coordinates and address, the dropoff coordinates and address, and manifest.total_value_cents — coordinates rounded to six decimal places, addresses whitespace- and case-normalised. Timestamps are deliberately excluded, so a second or two of drift between repeat clicks does not invalidate anything, while a changed address or basket total does.

The reuse window is driven by your expiration:

  • With an expiration, the quote is reused until 30 seconds before it lapses, so a caller always has a realistic chance of dispatching before your offer expires.
  • With no expiration, the quote is reused for two minutes from when it was issued.
  • A quote already linked to a dispatch is never reused.

Concurrency. Identical simultaneous requests are serialised behind a lock, so a burst produces one call to you rather than one per request. This matters most where quoting has a side effect on your side — reserving capacity, or creating a provisional job — but it saves everyone the load.

The practical consequence: set expiration honestly. Too long and customers see a price you will not honour. Too short and we re-quote you on every checkout keystroke.

Declining

Return any non-2xx. Declining is expected and is not treated as a fault.

HTTP/1.1 422 Unprocessable Entity

{ "error": "no_drivers_available", "message": "No couriers within range of the store" }

The channel receives a 200 carrying is_successful: false and your body under provider_response, and shows the customer that delivery is unavailable. Nothing is retried, and no order is affected — at quote time there is no order yet.

Common reasons to decline: no drivers in range, dropoff outside your service area, store closed on your side, order value above or below what you carry.

If you do not quote

Some providers have no pricing API — the delivery fee is whatever the brand charges, agreed commercially. That is a supported arrangement.

If that is you, either return fee_in_cents: 0 with the other fields null, or tell us during onboarding and we will not register a quote webhook at all. With no webhook registered, YUMBI Gateway records a placeholder quote and never calls you — the channel gets a quote with no fee, and dispatch proceeds normally.


Did this page help you?