Webhook Returns Edit the file on GitHub
Webhook Returns allows Skroutz Merchants to automatically receive return details to their platform when a new return is created or an existing one is updated (its state or logistics change), so the merchant's own system can stay in sync without polling the Returns API.
Webhook Returns is currently under development .
Table of Contents
Setup
In order to be able to use the webhook, a return webhook URL should be registered by the merchant from within Smart Cart settings page in merchant's panel (Merchants > Services > Skroutz Marketplace).
Registering a URL alone does not enable delivery — the return webhook must also be explicitly turned on from the same settings page. Both are required before any request is sent.
Webhook Returns requests are sent from the same infrastructure as Webhook Orders requests, so the same IP ranges apply:
IPv4:
- 185.6.76.0/22
- 3.73.204.153/32
- 3.72.204.195/32
- 3.67.183.221/32
- 63.34.193.172/32
- 54.195.53.34/32
- 108.129.50.199/32
IPv6:
- 2a03:e40::/32
Please ensure these IPs are whitelisted in your firewall or security tools to prevent webhook delivery failures.
You can also access the IP ranges in JSON format for programmatic consumption.
The webhook URL should match the shop's domain and it should follow secure HTTP (HTTPS)
Webhook URL example
| Shop URL | Webhook URL |
|---|---|
https://shop.gr |
https://shop.gr/smart_cart_returns |
Webhook requests
An HTTP POST request is sent to the predefined return webhook URL once an event has been triggered.
The anatomy of a request
Request
POST {webhook_url}
Request headers
| Header | Value |
|---|---|
Content-Type |
application/json; charset=utf-8 |
User-Agent |
Skroutz OrderNotifier v1 (shared with Webhook Orders) |
Since the
User-Agentvalue is shared with Webhook Orders, it cannot be used to distinguish order from return notifications. Use theevent_typefield in the request body instead.
Expected request response
200
Webhook events
The available webhook events for which a request is performed are:
- return created events
- return updated events
Return created
A return created webhook request (merchant_return_created) is triggered once, right after a
return is first placed.
Return updated
A return updated webhook request (merchant_return_updated) is triggered whenever:
- The return's state changes (e.g. approved by the merchant, marked as returning, returned,
rejected, expired or cancelled). If the update also makes shipment information newly available —
for example when the return transitions into the
returningstate — the payload additionally carries thecourierandreturning_addressfields. - The return's courier is reassigned on an existing return shipment.
- The return's courier tracking IDs become available.
- The deadline of the merchant's review changes, e.g. it is extended after the merchant contacts
the customer from the merchant panel. The payload carries the new
review.expires_at.
While a return waits for the merchant's review, its payload carries the review and the
handle_options to handle it
with. Their delivery choices are those available when the request was sent, so a request retried later
may list pickup windows that have passed: fetch the return for the current ones.
A return updated request is only sent once a return created request has already been delivered for that same return and webhook URL. In practice, if you enable the return webhook (or change its URL) after a return already exists, that return will not receive update events until a fresh return created event has been recorded against the new URL.
Request payload
| Name | Type | Value | Description |
|---|---|---|---|
event_type |
String | merchant_return_created, merchant_return_updated |
Return event type |
event_time |
Date String in formatYYYY-MM-DDTHH:MM:SSZ |
Event creation time | |
return |
Object | Return details | |
changes |
Object | Return changes with old and new values (optional) |
Return object
| Name | Type | Values | Description |
|---|---|---|---|
return_code |
String | Return Code | |
state |
String | pending_merchant_review, returning, pending_physical_review, pending_service_inspection, returned, rejected, expired, cancelled, resolved_without_return |
Current lifecycle state of the return. One of the return states. |
order_code |
String | The code of the order this return belongs to | |
created_at |
Date String in formatYYYY-MM-DDTHH:MM:SSZ |
Return creation date | |
courier_tracking_ids |
Array of Strings | (Optional) | Deduplicated tracking IDs across current shipments |
courier |
String | (Optional) | Human-readable name of the courier (e.g. ACS) |
returning_address |
Object | (Optional) | Returning address — the address the merchant should receive the return at |
return_line_items |
Array | Return line items — the products being returned | |
review |
Object | (Optional) | Review — the review waiting for the merchant |
handle_options |
Array | (Optional) | Handle options — the ways the merchant can handle the review |
courier_tracking_ids, courier and returning_address are omitted entirely (not sent as null)
when not applicable — e.g. virtual returns with no shipment.
review and handle_options are present only while the return is in state pending_merchant_review
and waits for the merchant's review. They are omitted once the review is handled or its deadline passes.
Return states
The state field is one of the following string values:
| State | Description |
|---|---|
pending_merchant_review |
The customer submitted a return request and the merchant needs to review it. |
returning |
The package is in transit back to the merchant. |
pending_physical_review |
The package arrived at the merchant and must perform physical inspection. |
returned |
The package was delivered back to the merchant. |
rejected |
The return was rejected. |
expired |
The merchant never responded to the return request within the deadline. |
cancelled |
The return was cancelled. |
pending_service_inspection |
A technician or an authorised service is to inspect the product. |
resolved_without_return |
The merchant sent the customer the missing part, so the product is not returned. |
Returning address
Present only when the return has a shipment.
| Name | Type | Description |
|---|---|---|
returning_address.street_name |
String | Street/road name |
returning_address.street_number |
String | Building number |
returning_address.zip |
String | Postal code |
returning_address.city |
String | City |
returning_address.region |
String | Region/state |
returning_address.country_code |
String | Country code (e.g. GR) |
Return line items
Each element of the return_line_items array represents one product line being returned.
| Name | Type | Values | Description |
|---|---|---|---|
id |
String | ID of the order line item being returned | |
ean |
String | EAN code of the product being returned | |
product_name |
String | Product display name | |
returning_quantity |
Integer | How many units of this product the customer is returning | |
return_reason |
String | faulty, wrong_product, withdrawal, damaged_product, expired_product, authenticity_doubt, delivery_issues, other (Optional) |
Reason the customer selected |
sub_reason |
String | different_product, missing_line_item, missing_part, wrong_delivered_size, content_mistake, damaged_courier_fault, damaged_merchant_fault (Optional) |
A more specific reason, e.g. missing_part for a wrong_product return |
requested_compensation |
String | refund, replace, repair, gift_card, none, consolation_coupon (Optional) |
What the customer asked for |
user_comment |
String | (Optional) | Free-text comment left by the customer explaining the return |
user_photos |
Array of Strings | (Optional) | Full URLs of photos uploaded by the customer as evidence |
user_videos |
Array | (Optional) | Videos uploaded by the customer as evidence |
User videos
| Name | Type | Description |
|---|---|---|
id |
String | The video ID |
url |
String | Watch the video (requires your API token) |
thumbnail_url |
String | The video's thumbnail (requires your API token) |
Review
Present while the return waits for the merchant's review.
| Name | Type | Values | Description |
|---|---|---|---|
review_type |
String | faulty, missing_part, out_of_policy_withdrawal, unopened_category_withdrawal, out_of_policy_wrong_size, unopened_category_wrong_size |
Why the return needs the merchant's review |
expires_at |
Date String in ISO 8601 format | Until when the merchant can handle the review. Afterwards Skroutz handles it, and the merchant may lose their right to compensation | |
holiday_auto_approval |
Boolean | Whether the withdrawal is out of policy only because of the holiday period, so Skroutz approves it when the deadline passes | |
customer_conversation_open |
Boolean | Whether a conversation with the customer about the product is open in the merchant panel |
Handle options
The options to send to handle the return:
approve, then send_missing_part, then the rejection reasons. Each option resolves the review. Contacting the customer,
which only extends the deadline, is available in the merchant panel only.
| Name | Type | Description |
|---|---|---|
option |
String | The value to send as option: approve, send_missing_part or a rejection reason |
comment_required |
Boolean | Rejection reasons only: whether comment is required |
supports_proposed_part_url |
Boolean | Rejection reasons only, present only when true: whether proposed_part_url can be sent |
delivery |
Object | send_missing_part only: the delivery options |
Rejection reasons
| Review type | Rejection reasons |
|---|---|
faulty |
contact_manufacturer (not offered while the product is within the dead on arrival period), usability_suggestions, not_faulty, resolved_via_customer_communication |
missing_part |
part_not_included, not_missing_part, resolved_via_customer_communication |
out_of_policy_withdrawal, unopened_category_withdrawal, out_of_policy_wrong_size, unopened_category_wrong_size |
out_of_policy_withdrawal |
| Option | Meaning |
|---|---|
approve |
The merchant accepts the return |
send_missing_part |
The merchant sends the missing part to the customer with an additional delivery, at the shop's cost, and the product is not returned. Offered for missing_part reviews when the order can get an additional delivery, but not for a product the merchant delivered with their own cargo carrier |
contact_manufacturer |
The customer needs to contact the manufacturer or the authorised service provider |
usability_suggestions |
The product has been used in a way that affects how it works |
not_faulty |
The product is not faulty: it works according to its specifications |
part_not_included |
The product was delivered complete as described: no part is missing. The merchant can point the customer to the part on Skroutz, e.g. when it is sold separately, with proposed_part_url |
not_missing_part |
The product page on Skroutz has wrong content that needs a correction |
resolved_via_customer_communication |
The merchant already contacted the customer and the issue has been resolved |
out_of_policy_withdrawal |
The withdrawal is outside the return policy |
Delivery options
The choices for the missing part's delivery, named after the params of
send_missing_part, as in an order's
accept options.
| Name | Type | Example | Description |
|---|---|---|---|
delivery.pickup_location |
Array | Pickup locations, the order's own first | |
delivery.weight |
Object | { "min": 1, "max": 50 } |
Valid weight range, in whole kilos |
delivery.number_of_parcels |
Array | [1, 2, 3] |
Valid values for number_of_parcels |
Delivery option: Pickup location
| Name | Type | Description |
|---|---|---|
pickup_location.id |
String | The location ID to send as pickup_location. The same IDs as in an order's accept options |
pickup_location.label |
String | A human readable address |
pickup_location.pickup_window |
Array | Pickup windows to send with cargo: false |
pickup_location.cargo_pickup_window |
Array | Pickup windows to send with cargo: true. Present only for an order shipped as a cargo item |
Delivery option: Pickup window
| Name | Type | Example | Description |
|---|---|---|---|
pickup_window.id |
String | "20261029T1000-20261029T1400" |
The pickup window ID to send as pickup_window |
pickup_window.label |
String | "10:00 - 14:00, Πέμ 29/10/26" |
A human readable time range |
As in an order's accept options, the id identifies the window by its start and end time
(YYYYMMDDTHHMM-YYYYMMDDTHHMM, local Greek time). Each window books its own courier, so there is
no courier to choose. The same id can appear in both lists of a location, so cargo tells which
one you picked.
Return changes object
Only present on return updated requests, and only includes the keys that actually changed.
| Field | Shape | Notes |
|---|---|---|
state |
{ old, new } |
Raw state values, see return object |
courier |
{ old, new } |
Human-readable courier name; old can be null when courier info first becomes available |
returning_address |
{ old, new } |
Same shape as returning address |
courier_tracking_ids |
{ old, new } |
Only included when a new value is actually present |
review_expires_at |
{ old, new } |
The review's deadline, as in review.expires_at |
Payload Examples
Example 1 (return created - waiting for the merchant's review)
{
"event_type": "merchant_return_created",
"event_time": "2026-08-18T09:12:03Z",
"return": {
"return_code": "R-111111-1111111",
"state": "pending_merchant_review",
"order_code": "48213907",
"created_at": "2026-08-18T09:12:03Z",
"return_line_items": [
{
"id": "gY6oNzr8jp",
"ean": "1234567890123",
"product_name": "Apple iPhone 15 128GB",
"returning_quantity": 1,
"return_reason": "wrong_product",
"sub_reason": "missing_part",
"requested_compensation": "replace",
"user_comment": "The charging cable was not in the box.",
"user_photos": [
"https://api.skroutz.gr/assets/returns/photo_1.jpg"
],
"user_videos": [
{
"id": "3f1c9a52d0e84b6f",
"url": "https://api.skroutz.gr/merchants/ecommerce/returns/R-111111-1111111/videos/3f1c9a52d0e84b6f",
"thumbnail_url": "https://api.skroutz.gr/merchants/ecommerce/returns/R-111111-1111111/videos/3f1c9a52d0e84b6f/thumbnail"
}
]
}
],
"review": {
"review_type": "missing_part",
"expires_at": "2026-08-19T18:00:00+03:00",
"holiday_auto_approval": false,
"customer_conversation_open": false
},
"handle_options": [
{ "option": "approve" },
{
"option": "send_missing_part",
"delivery": {
"pickup_location": [
{
"id": "YlpD0KROym",
"label": "Πανεπιστημίου 2, Τ.Κ. 10564, Αθήνα, Αττική",
"pickup_window": [
{ "id": "20260819T1000-20260819T1400", "label": "10:00 - 14:00, Τετ 19/08/26" },
{ "id": "20260819T1500-20260819T1800", "label": "15:00 - 18:00, Τετ 19/08/26" }
]
}
],
"weight": { "min": 1, "max": 50 },
"number_of_parcels": [1, 2, 3]
}
},
{ "option": "part_not_included", "comment_required": true, "supports_proposed_part_url": true },
{ "option": "resolved_via_customer_communication", "comment_required": false },
{ "option": "not_missing_part", "comment_required": true }
]
}
}
Example 2 (return updated - shipment info newly available)
{
"event_type": "merchant_return_updated",
"event_time": "2026-08-19T10:00:00Z",
"return": {
"return_code": "R-111111-1111111",
"state": "returning",
"order_code": "48213907",
"created_at": "2026-08-18T09:12:03Z",
"courier": "ACS",
"returning_address": {
"street_name": "Ermou",
"street_number": "12",
"zip": "10563",
"city": "Athens",
"region": "Attica",
"country_code": "GR"
},
"return_line_items": [
{ "id": "gY6oNzr8jp", "ean": "1234567890123", "product_name": "Apple iPhone 15 128GB", "returning_quantity": 1 }
]
},
"changes": {
"state": { "old": "pending_merchant_review", "new": "returning" },
"courier": { "old": null, "new": "ACS" },
"returning_address": {
"old": null,
"new": {
"street_name": "Ermou",
"street_number": "12",
"zip": "10563",
"city": "Athens",
"region": "Attica",
"country_code": "GR"
}
}
}
}
Example 3 (return updated - courier tracking IDs become available)
{
"event_type": "merchant_return_updated",
"event_time": "2026-08-19T11:30:00Z",
"return": {
"return_code": "R-111111-1111111",
"state": "returning",
"order_code": "48213907",
"created_at": "2026-08-18T09:12:03Z",
"courier": "ACS",
"courier_tracking_ids": ["ACS1234567890"],
"returning_address": {
"street_name": "Ermou",
"street_number": "12",
"zip": "10563",
"city": "Athens",
"region": "Attica",
"country_code": "GR"
},
"return_line_items": [
{ "id": "gY6oNzr8jp", "ean": "1234567890123", "product_name": "Apple iPhone 15 128GB", "returning_quantity": 1 }
]
},
"changes": {
"courier_tracking_ids": { "old": null, "new": ["ACS1234567890"] }
}
}
Example 4 (return updated - courier reassignment)
{
"event_type": "merchant_return_updated",
"event_time": "2026-08-19T12:30:00Z",
"return": {
"return_code": "R-111111-1111111",
"state": "returning",
"order_code": "48213907",
"created_at": "2026-08-18T09:12:03Z",
"courier": "Speedex",
"courier_tracking_ids": ["ACS1234567890"],
"returning_address": {
"street_name": "Ermou",
"street_number": "12",
"zip": "10563",
"city": "Athens",
"region": "Attica",
"country_code": "GR"
},
"return_line_items": [
{ "id": "gY6oNzr8jp", "ean": "1234567890123", "product_name": "Apple iPhone 15 128GB", "returning_quantity": 1 }
]
},
"changes": {
"courier": { "old": "ACS", "new": "Speedex" }
}
}
Example 5 (return updated - plain state change)
{
"event_type": "merchant_return_updated",
"event_time": "2026-08-19T14:03:11Z",
"return": {
"return_code": "R-111111-1111111",
"state": "returned",
"order_code": "48213907",
"created_at": "2026-08-18T09:12:03Z",
"courier": "Speedex",
"courier_tracking_ids": ["ACS1234567890"],
"returning_address": {
"street_name": "Ermou",
"street_number": "12",
"zip": "10563",
"city": "Athens",
"region": "Attica",
"country_code": "GR"
},
"return_line_items": [
{
"id": "gY6oNzr8jp",
"ean": "1234567890123",
"product_name": "Apple iPhone 15 128GB",
"returning_quantity": 1
}
]
},
"changes": {
"state": { "old": "returning", "new": "returned" }
}
}
Example 6 (return updated - review deadline extended)
The handle_options are shortened here; they are the same as in Example 1.
{
"event_type": "merchant_return_updated",
"event_time": "2026-08-18T11:30:00Z",
"return": {
"return_code": "R-111111-1111111",
"state": "pending_merchant_review",
"order_code": "48213907",
"created_at": "2026-08-18T09:12:03Z",
"return_line_items": [
{
"id": "gY6oNzr8jp",
"ean": "1234567890123",
"product_name": "Apple iPhone 15 128GB",
"returning_quantity": 1,
"return_reason": "wrong_product",
"sub_reason": "missing_part",
"requested_compensation": "replace"
}
],
"review": {
"review_type": "missing_part",
"expires_at": "2026-08-20T18:00:00+03:00",
"holiday_auto_approval": false,
"customer_conversation_open": true
},
"handle_options": [
{ "option": "approve" },
{ "option": "part_not_included", "comment_required": true, "supports_proposed_part_url": true }
]
},
"changes": {
"review_expires_at": { "old": "2026-08-19T18:00:00+03:00", "new": "2026-08-20T18:00:00+03:00" }
}
}