Skip to content
Developers

REST API

The WPReturns REST API lets other tools read and manage return requests under the ddwcwr/v1 namespace. Routes cover requests (list, create and update), messages (read and post) and the reason list. Every route needs a user with the ddwcwr_manage_requests capability, usually through an application password.

Available in: Pro.

Use a WordPress application password for calls from outside WordPress.

  1. Pick a user who can manage requests such as a shop manager. Administrators also pass.
  2. Go to Users > Profile and scroll to Application Passwords.
  3. Enter a name such as “Helpdesk” and select Add New Application Password.
  4. Copy the password. WordPress shows it only once.

Send it with HTTP Basic auth over HTTPS:

Terminal window
curl -u "shopmanager:abcd efgh ijkl mnop qrst uvwx" \
https://example.com/wp-json/ddwcwr/v1/requests

Code running inside wp-admin can use the logged-in cookie with a wp_rest nonce instead.

Calls without the capability get rest_forbidden with the message “You are not allowed to manage return requests.”

MethodRouteWhat it does
GET/wp-json/ddwcwr/v1/requestsList requests
POST/wp-json/ddwcwr/v1/requestsCreate a request
GET/wp-json/ddwcwr/v1/requests/{id}Get one request
PUT, PATCH or POST/wp-json/ddwcwr/v1/requests/{id}Update status, assignee or tracking
GET/wp-json/ddwcwr/v1/requests/{id}/messagesList messages, internal notes and log entries
POST/wp-json/ddwcwr/v1/requests/{id}/messagesAdd a message or internal note
GET/wp-json/ddwcwr/v1/reasonsList active return reasons

{id} is the numeric request ID. RMA-01042 has the ID 1042.

GET /requests takes these query parameters.

ParameterTypeNotes
statusstringA status slug such as pending or received
typestringreturn, exchange, warranty, withdrawal or cancellation
order_idintegerRequests for one order
customerintegerRequests of one customer user ID
searchstringRMA or order number, or text in email, name or tracking number
pageintegerDefault 1
per_pageintegerDefault 20 and at most 100

The response headers X-WP-Total and X-WP-TotalPages give the totals.

Terminal window
curl -u "shopmanager:abcd efgh ijkl mnop qrst uvwx" \
"https://example.com/wp-json/ddwcwr/v1/requests?status=received&per_page=10"

Every route that returns a request uses this shape. Webhooks send the same object.

{
"id": 1042,
"number": "RMA-01042",
"order_id": 1042,
"customer_id": 17,
"customer_email": "jane.cooper@example.com",
"customer_name": "Jane Cooper",
"type": "return",
"status": "received",
"status_label": "Received",
"resolution": "refund",
"customer_note": "The shirt is too small.",
"items_total": 49,
"restocking_fee": 0,
"shipping_fee": 5,
"credit_bonus": 0,
"resolution_total": 44,
"refunded_total": 0,
"coupon_code": "",
"exchange_order_id": 0,
"tracking_number": "1Z999AA10123456784",
"carrier": "UPS",
"assigned_to": 0,
"risk_score": 0,
"risk_flags": [],
"source": "account",
"returnless": false,
"date_created": "2026-11-02T10:15:00+00:00",
"date_updated": "2026-11-06T14:40:00+00:00",
"date_closed": null,
"items": [
{
"id": 2210,
"order_item_id": 3381,
"product_id": 512,
"variation_id": 515,
"name": "Linen shirt - M",
"qty": 1,
"line_total": 49,
"reason": "wrong-size",
"condition": "unopened",
"exchange_product_id": 0,
"restocked": 1
}
]
}

source is account, portal, admin, api or withdrawal. Money fields are numbers in the order currency.

POST /requests creates a request as staff. It takes a JSON body.

FieldRequiredNotes
order_idYesThe WooCommerce order ID
itemsYesArray of order_item_id, qty, reason, condition and exchange_product_id
typeNoDefault return. Any type except withdrawal makes sense here.
resolutionNorefund, store_credit, exchange, repair, replace or keep. Empty picks the first allowed one.
noteNoSaved as the customer’s note
Terminal window
curl -u "shopmanager:abcd efgh ijkl mnop qrst uvwx" \
-H "Content-Type: application/json" \
-X POST https://example.com/wp-json/ddwcwr/v1/requests \
-d '{
"order_id": 1042,
"type": "return",
"resolution": "refund",
"items": [
{ "order_item_id": 3381, "qty": 1, "reason": "wrong-size", "condition": "unopened" }
],
"note": "Customer called the shop."
}'

The response is the new request with status 201. Like requests made on the New request screen, API requests skip order status checks, return windows and auto decisions.

Each item still needs a valid reason slug for returns, exchanges and warranty claims. Quantities stay capped at what is left on the order line.

Errors return status 400 with the message, for example “Please choose a reason for Linen shirt - M.”

PUT /requests/{id} changes these fields. Send only the ones you want to change.

FieldNotes
statusAny status slug. Customers get the email for it when Notify is on.
noteSent with the status email and added to the conversation
assigned_toA user ID, or 0 to unassign
tracking_numberThe return tracking number
carrierThe carrier name
Terminal window
curl -u "shopmanager:abcd efgh ijkl mnop qrst uvwx" \
-H "Content-Type: application/json" \
-X PUT https://example.com/wp-json/ddwcwr/v1/requests/1042 \
-d '{ "status": "approved", "note": "Please use the prepaid label in your email." }'

Statuses listed under Restricted statuses need the ddwcwr_process_refunds capability. Without it the call fails with status 403: “You are not allowed to move requests to this status.”

GET /requests/{id}/messages returns the conversation, internal notes and the history log.

[
{
"id": 8812,
"kind": "message",
"author_type": "customer",
"author_id": 17,
"message": "The shirt is too small.",
"attachments": 2,
"date": "2026-11-02T10:15:00+00:00"
}
]

kind is message, note or log. attachments is a count. Files are not served through the API.

POST /requests/{id}/messages adds a staff message. Set private to true for an internal note.

Terminal window
curl -u "shopmanager:abcd efgh ijkl mnop qrst uvwx" \
-H "Content-Type: application/json" \
-X POST https://example.com/wp-json/ddwcwr/v1/requests/1042/messages \
-d '{ "message": "Your parcel arrived today.", "private": false }'

The response is { "id": 8813 } with status 201. Messages are emailed to the customer. Notes are not.

GET /reasons returns the active reasons with slug, label, status, require_photo and merchant_fault. Use the slugs in the items[].reason field when you create a request.