REST API
ReturnDesk Pro has a REST API under the namespace tsre/v1, and three WooCommerce webhook topics. Staff routes manage returns, customer routes work on the signed-in customer's own returns, and two routes manage store credit.
The REST API and the webhook topics are part of the Pro edition. The Lite edition registers only the signed photo route GET /photos/{id} (see "Photos" below), which the return screen uses to show the customer's photos. Every other route answers 404 rest_no_route in Lite.
Base address: https://example.com/wp-json/tsre/v1
Authentication
| Routes | Who can call them |
|---|---|
/returns... | A signed-in user with the manage_tsre_returns capability (Administrator and Shop manager) |
/settings | A signed-in user with manage_woocommerce |
/me/returns... | Any signed-in user. A customer sees only their own returns, other ids answer 404. |
/credits/... (Pro) | A signed-in user with manage_tsre_returns |
/photos/{id} | A signed link, see "Photos" below |
/webhooks/{carrier} (Pro) | The carrier service, authenticated by signature, see Labels and carriers |
ReturnDesk uses the standard WordPress REST authentication:
- Application Passwords for scripts and other servers. Create one on the user profile and send it with HTTP Basic authentication over HTTPS.
- Cookie sessions for code running in the browser. Send the
X-WP-Nonceheader with thewp_restnonce.
curl -u "api-user:YOUR_APPLICATION_PASSWORD" \
"https://example.com/wp-json/tsre/v1/returns?status[]=requested&per_page=20"
Without a valid login, staff routes answer 401 (or 403 when you are signed in without the capability) with rest_forbidden, and /me routes answer 401 with rest_not_logged_in. The API has no rate limit of its own.
Staff routes
List returns
GET /returns
| Parameter | Type | Notes |
|---|---|---|
status | array | One or more of requested, approved, label_sent, in_transit, received, inspected, resolved, rejected, cancelled |
return_method | array | One or more of prepaid_label, customer_label, dropoff, in_store, keep |
search | string | Up to 200 characters. Matches the RMA number and the email, and the order id when numeric. |
customer_id, order_id | integer | Minimum 1 |
date_from, date_to | string | YYYY-MM-DD or YYYY-MM-DD HH:MM:SS, on the creation date. A date alone covers the whole day. |
page | integer | Default 1 |
per_page | integer | 1 to 100, default 20 |
orderby | string | created_at (default), updated_at, status, return_number, order_id |
order | string | asc or desc (default) |
The body is an array of returns. The headers X-WP-Total and X-WP-TotalPages give the totals.
Get a return
GET /returns/{id}
Returns the return with its items, its events (the timeline) and its photos.
{
"id": 39,
"return_number": "RMA-2026-000039",
"order_id": 74,
"order_number": "74",
"customer_id": 3,
"customer_email": "[email protected]",
"status": "requested",
"resolution": "refund",
"return_method": "customer_label",
"currency": "EUR",
"refund_total": 35.0,
"credit_total": 0.0,
"fee_total": 0.0,
"exchange_order_id": null,
"refund_id": null,
"label_id": null,
"reason_summary": "Wrong size or fit",
"customer_note": "The sleeves are a little too short for me.",
"admin_note": null,
"window_expires_at": "2026-11-27 18:40:00",
"created_at": "2026-10-28 16:40:00",
"updated_at": "2026-10-28 16:40:00",
"resolved_at": null,
"erased_at": null,
"items": [
{
"id": 61,
"return_id": 39,
"order_item_id": 12,
"product_id": 22,
"variation_id": 0,
"qty_requested": 1,
"qty_accepted": 0,
"qty_rejected": 0,
"reason_code": "wrong_size",
"reason_text": "",
"condition_grade": null,
"restock": false,
"unit_refund": 35.0,
"line_refund": 0.0
}
],
"events": [],
"photos": []
}
Dates are UTC. The guest access token is never part of the response.
Create a return
POST /returns starts a return for a customer's order, as the signed-in member of staff. The same eligibility rules apply as for the customer, so an item outside its window is refused.
| Field | Type | Notes |
|---|---|---|
order_id | integer | Required |
items | array | Required, 1 to 100 lines. Each line is an object with order_item_id and qty (both required, minimum 1), and optionally reason_code, reason_text (up to 500 characters), exchange_product_id and exchange_variation_id (Pro). |
resolution | string | Required: refund, credit, exchange or warranty |
return_method | string | Required: prepaid_label, customer_label, dropoff, in_store or keep |
note | string | Up to 2000 characters |
Any other field is rejected with 400. The answer is 201 with the new return.
curl -u "api-user:YOUR_APPLICATION_PASSWORD" -X POST \
-H "Content-Type: application/json" \
-d '{"order_id":74,"items":[{"order_item_id":12,"qty":1,"reason_code":"wrong_size"}],"resolution":"refund","return_method":"customer_label"}' \
"https://example.com/wp-json/tsre/v1/returns"
Move a return to a status
POST /returns/{id}/transition
| Field | Type | Notes |
|---|---|---|
to | string | Required: approved, rejected, in_transit, received or cancelled |
note | string | Up to 2000 characters. The internal note of an approval, or the reason of a rejection. |
tracking_number | string | Up to 100 characters, used with in_transit |
label_sent, inspected and resolved have their own flows (labels, the inspect route and the resolve route). A move the status machine does not allow answers 409 with tsre_invalid_transition. See Customer flow for the allowed moves.
Inspect a return
POST /returns/{id}/inspect records the inspection of a received return.
| Field | Type | Notes |
|---|---|---|
lines | array | Required. One object per returned item: order_item_id, qty_accepted and condition_grade are required, qty_rejected (default 0) and restock (boolean) are optional. |
condition_grade is new, opened, used or damaged. Unknown properties are rejected, and an item can only be listed once. Accepted plus rejected cannot exceed the requested quantity.
Resolve a return
POST /returns/{id}/resolve resolves an inspected return. The plan is rebuilt on the server, so the request carries no amounts to trust.
| Field | Type | Notes |
|---|---|---|
amount | number | A lower amount to give back instead of the whole plan, only on a refund or a store credit. An exchange or warranty claim answers 400 tsre_amount_not_allowed. |
recovery | string | accept, discard or retry: a decision about a refund that failed before, see Admin flow |
confirm | boolean | Must be true for a recovery decision, and to resolve a return that the customer keeps |
A recovery decision only works after a recorded failure of the matching kind. A resolution that fails answers 409 tsre_resolve_failed with the message.