MMelo Developers

Delivery flow

Learn how quotes become paid transfers and how to track their state.

A quote and a transfer are separate resources. The quote discovers available products and prices; the transfer commits to one product and charges the merchant balance.

Quote a delivery

Call POST /options with:

  • A pickup: an active warehouseId, or a custom pickupLocation
  • A destination: the coordinates and address in destinationLocation, or one of your warehouses as destinationWarehouseId
  • A parcel size (parcelSize) or item dimensions (items)
  • Fragile and perishable flags

Pickup and destination

Send exactly one pickup and exactly one destination. Any combination works:

destinationLocationdestinationWarehouseId
warehouseIdWarehouse to addressWarehouse to warehouse
pickupLocationCustom address to addressCustom address to warehouse

A custom pickup is any address, with the same shape as destinationLocation (lat, lng, display_name, city). Who the courier meets there does not affect the price, so you give it later, when you create the transfer.

Custom pickup to a warehouse
{
  "pickupLocation": {
    "lat": 41.7225,
    "lng": 44.7925,
    "display_name": "37 Chavchavadze Avenue, Tbilisi",
    "city": "Tbilisi"
  },
  "destinationWarehouseId": "33333333-3333-4333-8333-333333333333",
  "parcelSize": "SMALL",
  "isFragile": false,
  "isPerishable": false
}

Every warehouse you send, as pickup or destination, must belong to your merchant and be active, and the pickup and destination cannot be the same warehouse. Another merchant's warehouse returns 403 UNAUTHORIZED.

You are always the paying merchant

Whichever pickup and destination you choose, the transfer belongs to the merchant that requested the quote. Its balance is charged and refunded, its webhooks fire, and only it can read or cancel the transfer. A custom pickup does not change this.

When the destination is a warehouse, recipientInfo is still required on POST /transfer. It is the person Melo notifies and the courier calls on arrival.

Parcel sizes

Send parcelSize directly, or let Melo derive it from items.

ValueNameDescriptionMax dimensions
SMALLPocket ParcelSmall items like documents, keys, jewelry, or phone accessories.20cm × 15cm × 5cm
MEDIUMBag ParcelMedium items like clothing, books, or small electronics.40cm × 30cm × 20cm
LARGEBox SizeLarge items like appliances, furniture parts, or multiple items.80cm × 60cm × 40cm
CARGOCargo FreightVery large or heavy items requiring special handling.Custom dimensions and weight specifications

parcelSize is required unless items is sent with at least one entry. If both are present, parcelSize is used and items is ignored.

Deriving size from items

Instead of parcelSize, send items — one entry per distinct item, each with a dimensions object (length, width, height in centimeters, all positive numbers), an optional quantity (integer, minimum 1, default 1), and an optional title (up to 255 characters, not currently used). dimensions is required on every entry unless parcelSize is also sent:

"items": [
  { "dimensions": { "length": 20, "width": 15, "height": 10 }, "quantity": 2, "title": "Phone case" },
  { "dimensions": { "length": 12, "width": 8, "height": 4 } }
]

Each entry's dimensions are counted quantity times, then summed independently across all entries into one combined bounding box, which is classified (checked in any rotation) against the max dimensions above. In the example above, the first entry contributes 40×30×20 (20×15×10 doubled for quantity: 2) and the second contributes 12×8×4, summing to 52×38×24 — which classifies as LARGE.

The response contains a quote id and a products object of available delivery options — unrelated to the request's items array. Each key in the response's products is a valid productKey for transfer creation.

Persist the exact pair

Keep the quote id and selected product key together. A product key is meaningful only within the quote that returned it.

Quotes expire after 10 minutes

A quote can only be turned into a transfer within 10 minutes of being created. After that POST /transfer returns 400 QUOTE_EXPIRED and you must request a fresh quote. Quote at the point of purchase rather than caching prices.

Create and pay

Call POST /transfer using the quote ID as matchmakingId, the selected productKey, and recipient details.

If the quote starts from a custom pickupLocation, also send pickupContact: who the courier meets there, in the same shape as recipientInfo (firstName, lastName and a phone the courier can call). A warehouse supplies its own contact details, so pickupContact is required for a custom pickup and rejected for a warehouse pickup.

Create a transfer from a custom pickup
{
  "matchmakingId": "22222222-2222-4222-8222-222222222222",
  "productKey": "YOUR_PRODUCT_KEY",
  "recipientInfo": { "firstName": "Nino", "lastName": "Beridze", "phone": "+995555123456" },
  "pickupContact": { "firstName": "Giorgi", "lastName": "Kapanadze", "phone": "+995555987654" }
}

Getting this wrong returns 400 PICKUP_CONTACT_REQUIRED or 400 PICKUP_CONTACT_NOT_ALLOWED, and nothing is created: the quote stays pending, so correct the request and send it again within the quote's ten minutes.

The operation performs the following work atomically:

  1. Validates that the quote exists, is still pending, and has not expired.
  2. Resolves the selected product and payable amount.
  3. Creates the transfer, parcel, delivery metadata, and tracking steps.
  4. Subtracts the amount from the merchant balance.
  5. Marks the quote as completed.

A completed quote cannot be used again. If the request succeeds but your client loses the response, do not blindly retry with the same quote. Query your internal records or contact Melo support before creating a new quote.

Track state

Call GET /transfer/{transferId} to retrieve the current state:

StateMeaning
inProgressThe delivery is active.
completedThe delivery reached its final successful state.
failedThe delivery ended unsuccessfully.

The response also contains the public trackingCode, the parcel size, pickup and destination details, and the selected delivery option. Pickup and destination fields are omitted when a transfer has no delivery metadata.

warehouseId is the pickup warehouse and is null for a custom pickup. destinationWarehouseId is the destination warehouse and is null when the transfer was delivered to an address. The pickup and destination address fields are filled in either way.

Steps

GET /transfer/{transferId} also returns a steps array — the transfer's fulfilment history, which is more precise than state:

FieldDescription
stepcreated, payed, shipped, or delivered.
orderPosition in the workflow, 0 through 3.
statuscompleted, pending, or failed.
occuredAtWhen the step reached its current status.

A delivery is finished when delivered is completed. Any step reaching failed also moves state to failed.

You can be pushed these updates

Rather than polling, register a webhook to receive an event each time a transfer's steps change. See Webhooks.

Cancel a transfer

Call POST /transfer/{transferId}/cancel to cancel a transfer before shipping starts. Send an empty JSON object ({}) as the body.

A transfer can be cancelled only while it is at the created or payed step. Once a courier picks up the parcel, it can no longer be cancelled.

Current stepCancellable
created (awaiting payment)Yes
payed (paid, waiting for courier pickup)Yes
shipped (courier picked up the parcel)No
deliveredNo
Already failed or cancelledNo

On cancel:

  • The first pending step gets status failed. For a paid transfer, that is the shipped step.
  • The transfer state becomes failed.
  • If the transfer was paid, its price is refunded to your merchant balance.
  • A webhook event fires, with the same payload as any other step change.

The response contains the transfer id, state, and steps. A later GET /transfer/{transferId} returns the same values.

curl -X POST "https://api.melo.delivery/v1/transfer/3f1c2a8e-5b7d-4e0a-9c6f-1a2b3c4d5e6f/cancel" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Cancelling after pickup

Once shipping has started, the request returns 409 TRANSFER_NOT_CANCELLABLE. Do not retry it; contact Melo support instead.

  • Store Melo's transfer ID with your own order ID.
  • Store the tracking code for customer-facing support.
  • Poll with backoff rather than on every page view, and stay within the rate limits described in Errors.
  • Prefer webhooks for timely updates, reconciling with a periodic poll.
  • Treat network timeouts as an unknown outcome, especially for transfer creation.
  • Log Melo error type values, but redact credentials and personal data.

On this page