Create a return
Books a return shipment against an existing order: the courier collects from the buyer and delivers back to you. It is a shipment like any other, needing a rate, a courier and a service level, but it hangs off the order so the platform can tie the return to what was originally sold.
Get rates for the return the same way you would for an outbound shipment, with
POST /v2/rates: the collection address is the buyer's
and the delivery address is yours.
Request
Nothing is copied from the order: send both addresses, even though the collection address is usually the order's delivery address.
| Field | Type | Required | Notes |
|---|---|---|---|
order_id |
integer | yes | The order being returned against. |
provider_slug |
string | yes | The courier, from the rate you selected. |
service_level_code |
string | yes | The service level, from the rate you selected. |
collection_address |
object | yes | Where the courier collects, usually the buyer. Not needed when you send collection_pickup_point_location_id. |
delivery_address |
object | yes | Where the goods come back to. Not needed when you send pickup_point_location_id. |
parcels |
array | yes | What is coming back. See parcel fields. |
items |
array | no | The order lines being returned, as { "order_item_id": …, "qty": … }. Each must be a line on this order, or the request is refused with 400. Leave it out to return every line in full. |
declared_value |
number | no | Value of the contents in ZAR. |
selected_rate_request_id |
integer | no | The id from the rates response, for the funds check. |
collection_min_date |
timestamp | no | Earliest date the courier may collect. |
collection_after / collection_before |
string | no | Collection window, hh:mm:ss. |
instructions_collection |
string | no | Note for the collection driver. |
instructions_delivery |
string | no | Note for the delivery driver. |
collection_contact_name |
string | no | Who the courier collects from. First name. |
collection_contact_surname |
string | no | |
collection_contact_mobile_number |
string | no | |
collection_contact_email |
string | no | Checked when you send it. |
delivery_contact_name |
string | no | Who receives it. First name. |
delivery_contact_surname |
string | no | |
delivery_contact_mobile_number |
string | no | |
delivery_contact_email |
string | no | Checked when you send it. |
custom_tracking_reference |
string | no | Your own reference. |
shipping_label_emails |
array of string | no | Email the label to these addresses. |
pickup_point_location_id |
integer | no | Return to a pickup point. |
collection_pickup_point_location_id |
integer | no | Collect from a pickup point. |
Response
See the example response.
The return is created as a fulfilment on the order, with the return shipment
alongside it. shipment.tracking_reference is what you track, and
shipment.submission_status tells you whether it reached the courier. It is
asynchronous here exactly as it is on
POST /v2/shipments.
A return whose shipment could not be created still comes back 200. Check
order_fulfillment.status: on failed, order_fulfillment.failed_reason says
why. It usually comes back pending and can still fail afterwards, for example
with no-rates on a service level that was not quoted for the lane, so read it
again with GET /v2/order-fulfillments.
A return does not send the fulfillment/created webhook. Its shipment sends
shipment_submission_status/updated and tracking/updated like any other
shipment. See Webhooks.