Calculate delivery prices
This guide is for an integrator — or an AI agent working through the MCP server — who needs
to know the delivery fee for an address before creating a DELIVERY order.
The flow at a glance
Why
POST /v1/orders stores delivery_price verbatim: whatever value you send on a DELIVERY
order is the value that gets charged, with no server-side recalculation. The merchant
configures how delivery is priced from the OlaClick panel, under Settings → Delivery →
"Configure delivery prices and coverage". The API exposes that configuration and a quote
endpoint so you can resolve the right number — nothing in the API creates or edits the
pricing configuration itself.
The six panel options and what they mean for you
| Panel option | API mode | What you send |
|---|---|---|
| Free delivery | FIXED with fixed_price: 0 | Nothing — use fixed_price from the pricing configuration directly |
| Fixed price | FIXED | Nothing — use fixed_price from the pricing configuration directly, no quote call needed |
| Destination district | BY_DISTRICT | A district_id picked from districts[] — the price is already in that list, no quote call needed |
| Travelled distance | BY_DRIVE_DISTANCE | latitude and longitude |
| Custom areas | BY_AREA | latitude and longitude (the merchant must have Google Maps address validation enabled) |
| Custom ranges | BY_RANGE | latitude and longitude |
One flow per pricing mode
Each diagram starts after GET /v1/delivery/pricing has told you the mode.
Free delivery
Fixed price
No quote call is needed; the quote endpoint would return the same number.
Destination district
A quote with an unknown or disabled district_id answers price: 0 with area_id: null — that is a
request to fix, not a free delivery.
Travelled distance
Custom areas
Custom ranges
Step 1 — read the configuration
GET /v1/delivery/pricing
Required scope: orders:read
curl https://public-api.olaclick.app/v1/delivery/pricing \
-H "Authorization: Bearer olk_live_..."
The response shape depends on mode. The fields not relevant to the current mode come back
null or empty.
FIXED
{
"data": {
"active": true,
"mode": "FIXED",
"fixed_price": 5,
"distance": null,
"ranges": [],
"districts": [],
"accepts_out_of_coverage": false
}
}
BY_DISTRICT
{
"data": {
"active": true,
"mode": "BY_DISTRICT",
"fixed_price": null,
"distance": null,
"ranges": [],
"districts": [
{ "id": "6b2f1a04-1c7e-4a2b-9f11-2d8e0b3c4d55", "name": "Miraflores", "price": 6 },
{ "id": "9c4f4203-9e2c-403d-88cc-7c0ce9e0f572", "name": "San Isidro", "price": 8 }
],
"accepts_out_of_coverage": false
}
}
BY_DRIVE_DISTANCE
{
"data": {
"active": true,
"mode": "BY_DRIVE_DISTANCE",
"fixed_price": null,
"distance": { "starting_price": 3, "price_per_km": 1.5, "max_distance_km": 12 },
"ranges": [],
"districts": [],
"accepts_out_of_coverage": true
}
}
BY_RANGE
{
"data": {
"active": true,
"mode": "BY_RANGE",
"fixed_price": null,
"distance": null,
"ranges": [
{ "min": 0, "max": 3000, "price": 4 },
{ "min": 3000, "max": 10000, "price": 7 }
],
"districts": [],
"accepts_out_of_coverage": false
}
}
Response fields:
| Field | Description |
|---|---|
active | Whether delivery is enabled for this company |
mode | FIXED, BY_DISTRICT, BY_DRIVE_DISTANCE, BY_AREA, or BY_RANGE |
fixed_price | Flat delivery price. Populated only when mode is FIXED |
distance | { starting_price, price_per_km, max_distance_km }. Populated only when mode is BY_DRIVE_DISTANCE |
ranges | Distance tiers from the store, min/max in meters, ordered by max ascending. Populated only when mode is BY_RANGE |
districts | Deliverable districts with their fixed price. Populated only when mode is BY_DISTRICT |
accepts_out_of_coverage | When true, an address outside the configured coverage still gets a price of 0 from the quote instead of a 422 |
Step 2 — get a quote
BY_AREA, BY_DRIVE_DISTANCE, and BY_RANGE don't carry a price on GET /v1/delivery/pricing
— call the quote endpoint with the address's coordinates:
GET /v1/delivery/quote
Required scope: orders:read
| Query parameter | Description |
|---|---|
latitude, longitude | Decimal degrees. Send both or neither |
district_id | A district_id from districts[]. Only for BY_DISTRICT; coordinates sent alongside it are ignored in that mode. Omitting it (or sending an unknown or disabled district) returns price: 0 with area_id: null |
curl "https://public-api.olaclick.app/v1/delivery/quote?latitude=-34.5955&longitude=-58.3930" \
-H "Authorization: Bearer olk_live_..."
{
"data": {
"price": 6.5,
"area_id": "6b2f1a04-1c7e-4a2b-9f11-2d8e0b3c4d55",
"area_name": "Palermo"
}
}
area_id and area_name identify the matching area (BY_AREA), range (BY_RANGE) or
district (BY_DISTRICT); for BY_DRIVE_DISTANCE and FIXED they are null.
When the quote is 0
A price: 0 is a legitimate answer in two cases and a sign of a bad request in a third, so
check the pricing configuration before passing it to POST /v1/orders:
| Situation | How to tell |
|---|---|
| Free delivery | mode is FIXED and fixed_price is 0 |
| Outside coverage, but the merchant accepts it | mode is BY_AREA, BY_RANGE or BY_DISTRICT, accepts_out_of_coverage is true and area_id is null |
BY_DISTRICT quote without a valid district_id | mode is BY_DISTRICT and area_id is null: the district was omitted, unknown or disabled. Pick a district_id from districts[] and quote again — or skip the quote and use that district's price directly |
Never create a DELIVERY order with delivery_price: 0 unless one of the first two rows
applies.
Errors
| Status | Cause | Detail |
|---|---|---|
| 422 | Neither coordinate sent, but the pricing mode requires them | "This company's delivery pricing mode requires latitude and longitude." |
| 422 | Only one of latitude/longitude sent | Local validation error — they must be provided together |
| 422 | A coordinate out of range (latitude outside ±90, longitude outside ±180) | Local validation error naming the field |
| 422 | Address outside the configured coverage and the merchant does not accept out-of-coverage deliveries (accepts_out_of_coverage: false) | location_out_of_business_range. When accepts_out_of_coverage is true there is no error: the quote returns 200 with price: 0 and area_id: null |
| 422 | Pricing not fully configured for this company | "Delivery pricing is not fully configured for this company." — e.g. BY_AREA without Google Maps address validation enabled, no districts/areas configured, or the merchant has no coordinates of its own |
| 422 | Coordinates can't be routed | The routing provider's own message, e.g. "Coordinates specified are too far apart" — this typically means latitude and longitude were swapped |
| 404 | The company has no e-commerce settings | Nothing to quote against |
Where the coordinates come from
latitude/longitude have to come from somewhere other than the API — it does not geocode a
text address. Typical sources:
- The end customer's device (browser/app geolocation)
- An address autocomplete or map picker on the integrator's own side
- An address already saved in OlaClick:
GET /v1/addresses?client_id=...returnslatitude/longitudefor each saved address, andPOST /v1/addresseslets you save new ones with coordinates
Step 3 — create the order
Once you have the price, create the DELIVERY order with it:
POST /v1/orders
{
"service_type": "DELIVERY",
"client_id": "9c8e720f-31b1-485a-85fa-0aaf552faaa9",
"address_id": "6b2f1a04-1c7e-4a2b-9f11-2d8e0b3c4d55",
"delivery_price": 6.5
}
Send an Idempotency-Key header so a retried request can't create a duplicate order. See the
REST API Reference for the full request and response shape, and the
MCP Server page for the client and address tools.
Doing it through the MCP server
The same flow is exposed as MCP tools:
| Tool | What it does |
|---|---|
get_delivery_pricing | Read the company's delivery pricing configuration |
get_delivery_quote | Get a delivery price quote for one address |
list_addresses | List a client's saved delivery addresses |
create_address | Save a new delivery address (with coordinates) for a client |
create_order | Create the DELIVERY order with the quoted delivery_price |
Agent flow: pricing → address with coordinates → quote → order. Call get_delivery_pricing
first to learn the mode, resolve or save an address with coordinates (list_addresses /
create_address), call get_delivery_quote, then create_order with the returned price.
Apply the same 0 checks as above before creating the order, and pass an idempotency_key
to create_order so a retry after a timeout replays the same order instead of creating a
second one.
Needed scopes: orders:read (pricing and quote), addresses:read / addresses:write
(reading or saving addresses), orders:write (creating the order).
Example prompts:
"How does my restaurant charge for delivery?"
"How much would delivery cost to latitude -34.5955, longitude -58.3930?"
"Create a delivery order for client 9c8e720f-31b1-485a-85fa-0aaf552faaa9 to their default address and include the delivery fee"
See the MCP Server page for every tool's full parameters.
Worked example
A merchant priced BY_DRIVE_DISTANCE (starting_price: 3, price_per_km: 1.5,
max_distance_km: 12), using Buenos Aires coordinates:
| Address | Coordinates | Result |
|---|---|---|
| Near the store | -34.5955, -58.3930 | A low price — close to starting_price |
| Farther away | -34.5810, -58.4210 | A higher price, scaled by distance |
| Out of range | -34.9214, -57.9545 | 422 location_out_of_business_range, or price: 0 with area_id: null if the merchant accepts out-of-coverage deliveries |