How to use the refunds flow
14 minute read
What you’ll learn
In this how to we take you through how to perform cancellations and returns using the refund workflow, specifically:
- Key concepts and terms
- Processes and scenarios for common cancellation and return use cases
- The GraphQL API calls you’ll need to make
Key Terms
Before launching into the processes that can be followed, it’s worth clarifying the language and terms used when we talk about refunds and returns.
| Term | Description |
|---|---|
| Cancellation (Process) | Cancellation occurs before purchased items are dispatched. So for example, a Seller may wish to cancel the order if they have run out of stock. |
| Return (Process) | This is the term we use when an order has been dispatched, and the customer wishes to return it for a refund. An example of this is that the item did not meet the customers expectations and they do not want a replacement. By using the advanced workflow, sellers and operators can choose where they want the item to be returned or not. |
| Exchange (Process) | The Exchange process occurs when the order has been dispatched and the Customer wishes to swap (exchange) it for another item. For example the dispatched item was faulty, and the customer would like a replacement. Exchanging is not currently supported. |
| Refund Request (Entity) | A Refund Request is created when either the Cancellation or Return process is initiated. It is the primary vehicle used to manage the lifetime of this request. A successful Refund Request will ultimately result in an Invoice Amendment. |
| Invoice Amendment (Entity) | An Invoice Amendment will be made to a customer invoice if there needs to be any kind of adjustment following a successful Refund Request, (either the Cancellation or Return process). Note that an Invoice Amendment can be created directly on an Invoice without needing to create a Refund Request, (“short circuiting the process”). |
Workflow overview
The refunds flow is depicted below:

Happy path
- Create: The creation of the Return or Cancellation Request
- Return (optional): Requires that items that have been dispatched, need to be returned to the seller
- Accept (optional): Can be used:
- When items are required to be returned - accept that they have been successfully
- When items are not required to be returned - accept that refund is ok to progress to the next step
- Refund: Finalize the refund
Unhappy path
- Deny (optional): Can be used in multiple scenarios:
- Operator or Seller rejects the creation of a Refund Request
- Operator of Seller rejects the return step of a Refund Request
- Operator rejects the acceptance step of a Refund Request
- Deny (optional): Operator chooses to revert an approved Refund Request
Key takeaways
- Refunds can be processed individually at the Line Item Level (as opposed processing an entire request that may contain many line items)
- The flow is flexible in terms of how it is executed, so can be tailored for different paths and scenarios (discussed below)
Common Use Cases
To further illustrate how the refund workflow can be used, we’ll run through a number of common scenarios.
| Scenario Id | Type | Require Items to be returned | Seller Approval required | Mark Items as Returned |
|---|---|---|---|---|
| 1 | Cancellation | N/a | No | N/a |
| 2 | Cancellation | N/a | Yes | N/a |
| 3 | Return | No | No | N/a |
| 4 | Return | No | Yes | N/a |
| 5 | Return | Yes | Yes | Yes |
| 6 | Return | Yes | No | Yes |
All use cases will start with the same base condition(s):
- An Order has successfully been created with 1 line item
- For the Return scenarios, the line item will have been dispatched
GraphQL Mutations
The GraphQL mutations we’ll be working with are summarized below:
Note
Executable examples of these mutations can be found in our API collections.| mutation | Description | Operator | Seller |
|---|---|---|---|
refundRequestCreate | Create the refund or cancellation request. This mutation accepts a line item status that drives the remaining workflow steps, and is a key concept in the workflow. We will be covering this in more detail later. | Yes | Yes |
refundRequestLineItemAccept | Required when the Seller needs to provide “approval” of the return or cancellation request. This can be used alone, (when items are not required to be returned), or in combination with refundRequestLineItemReturn (when items are required to be returned). | Yes | Yes |
refundRequestLineItemReturn | Used when line items are required to be returned | Yes | Yes |
refundRequestLineItemDeny | Used to deny the request at a line item level | Yes | Yes - depends on status |
refundRequestRefund | Used to approve the request. However, refundRequestApprove provides a wider set of functionality. | Yes | No |
refundRequestApprove | This is a direct replacement to refundRequestRefund, allowing the Operator a greater degree of control when approving refunds and cancellations. | Yes | No |
returnShipmentCreate | Used to create return shipments, so you can pair this with refundRequestLineItemReturn when items are required to be returned. The use of this mutation is optional and is not on the critical path of the “returns flow” | Yes | Yes |
Creation Statuses
As mentioned above, the refundRequestCreate mutation accepts a status for each line item that is added to the request, an example of this mutation is shown below:
mutation {
refundRequestCreate(
input: {
invoiceId: "SW52b2ljZS0xMDAxOA=="
lineItems: [
{
lineItemId: "TGluZUl0ZW0tMTk="
quantity: 1
reason: "Milk has gone sour"
status: PENDING_APPROVAL
}
]
notes: [{ note: "Do not require item return" }]
}
) {
errors {
field
messages
}
refundRequest {
id
status
}
}
}
Here you can see this request adds 1 line item to the request with a status of PENDING_APPROVAL, this means that the Seller will need to determine how they want to process the request, this could be either:
- Just accept the request (
refundRequestLineItemAccept) - Require that the items are returned (
refundRequestLineItemReturn+refundRequestLineItemAccept)
The takeaway point is that this status determines next available steps in the overall workflow, you would select a different status depending on how you wanted the request to be processed. The complete list of statuses you can supply here are:
| Status | Description |
|---|---|
REFUND_ACCEPTED | It has already been determined that no return items are required, you can just approve the request |
PENDING_APPROVAL | Seller can determine how they want to process the request |
AWAITING_RETURN | It has been determined that return items are required, Seller needs to accept the items |
Use Case Examples
Pulling this information together, you can observe the mutation calls you’d need to make to cover all our scenarios, noting that we have abbreviated the full mutation name for a simpler label as follows:

Note: Be sure to pay attention to status used with each call to Create (refundRequestCreate).

State Transitions
Before we move on to a worked example, the last concept that you should be aware of are the various states that each of the primary objects participating in the refund request can take. Those objects are:
| Object | Description | |
|---|---|---|
Order | The parent object for the entire Marketplacer order. An order contains 1 or more Invoices, one for each Seller | |
Invoice | The object relating to the Sellers part of the Order, and a primary participant in the refund flow | |
LineItem | Represents products that were purchased with the creation of the Order and Invoice. | |
RefundRequest | This object is created with a successful call to refundRequestCreate. It is the primary vehicle by which the entire refund request is tracked. Note that 1 RefundRequest can contain many RefundRequestLineItems | |
RefundRequestLineItem | This object is created along with the RefundRequest, and represents the individual refund components which can be Invoice Line Items, (see above), or Custom Line items, representing “non-product” refund components, e.g. Postage costs etc. In this article we are dealing exclusively with RefundRequestLineItem objects that were created using an Invoice Line Item. |
A simple example of the relationship between these objects is shown below:

All of the objects above have state, and in our worked example we’ll provide state transitions for all of them, however for the moment we’ll focus in on the states that the following objects can have:
- RefundRequest
- RefundRequestLineItem
RefundRequest States
The RefundRequest object can have the following states:
| State | Description |
|---|---|
| Awaiting | There is still some action required to be taken on the refund request |
| Processed | All refund request line items have been actioned by the Seller and it is now for the Operator to action |
| Refunded | The refund request has been finalized (note if some line items are refunded and some are denied, the status will still show as refunded) |
| Denied | The refund request has been denied (note, only when all refund request line items are denied |
RefundRequestLineItem States
The RefundRequestLineItem object can have the following states:
| State | Description |
|---|---|
| Pending Approval | The line item is pending an action by the Seller |
| Awaiting Return | The line item has been requested to be returned to the Seller |
| Refund Accepted | The line item has been authorized by the seller and they have completed the actions required to progress this refund request to the operator |
| Refunded | The line item has been refunded |
| Denied | The line item has been denied |
RefundRequest & RefundRequestLineItem State Correlation
The matrix below shows the correlation between RefundRequest and RefundRequestLineItem states:

Worked Example
The following worked example follows Scenario 5 which utilizes all of the new and updated mutations. You should be able to take this example and adapt it easily to the remaining 5 uses-cases.
Note
The Postman and Insomnia collections found here contain examples of all the mutations we are going to use.As a reminder, Scenario 5 is as follows:

Note
In this example we are going to follow the “happy path” and not perform any denials. It’s also worth remembering that while we will create a return shipment usingreturnShipmentCreate this step is not technically part of the critical path of the return flow.Before we start
As this is a return scenario the following pre-conditions need to be met before we can begin with the refund flow:
- You will need a created order with 1 line item.
- For more information on creating orders in GraphQL refer to this article
- The line item on the order / invoice has been dispatched
- You can use the
shipmentCreateGraphQL mutation to achieve this or just dispatch the line item from the Seller Portal
- You can use the
At this point our primary objects should have the following state:
| Object | State | Notes |
|---|---|---|
| Order | COMPLETE | |
| Invoice | PAID, SENT | You should use Invoice statusFlags for the Invoice state |
| Line Item | ALLOCATED | |
| Refund Request | N/a | A Refund Request has not been created yet therefore there is no state |
| Refund Request Line Item | N/a | A Refund Request has not been created yet therefore there is no state |
We’ll additionally track the webhook events for the following webhook types as we move through out example:
- Refund Request
- Refund Request Line Item
Webhooks
For more information on what Webhooks are and how they can be employed in your solution, please refer to our docs here.| Webhook Event | Events | Notes |
|---|---|---|
| Refund Request | N/a | A Refund Request has not been created yet therefore there are no associated webhook events |
| Refund Request Line Item | N/a | A Refund Request has not been created yet therefore there are no associated webhook events |
Step 1: Call refundRequestCreate

The first step is to call refundRequestCreate, for this you will require:
- The
InvoiceId - The
LineItemId of the Line Item you want to return - The Line Item
statuswe want to use in this case this will be:PENDING_APPROVAL
This call can be made by:
- The Operator
- The Seller (requires additional permissions)
An example mutation call is shown below:
mutation {
refundRequestCreate(
input: {
invoiceId: "SW52b2ljZS0xMDAxOA=="
lineItems: [
{
lineItemId: "TGluZUl0ZW0tMTk="
quantity: 1
reason: "Cracked Screen"
status: PENDING_APPROVAL
}
]
notes: [{ note: "High value item, requires return" }]
}
) {
errors {
field
messages
}
refundRequest {
id
status
lineItems {
id
status
}
}
}
}
A successful call will result in the following:
RefundRequestIdRefundRequestLineItemId (we’ll need this for the next step)
Our primary objects should have the following state:
| Object | State | Notes |
|---|---|---|
| Order | COMPLETE | |
| Invoice | PAID, SENT, AWAITING_RETURN | We have an additional status flag of AWAITING_RETURN |
| Line Item | ALLOCATED | |
| Refund Request | AWAITING | Transitioned from “N/a” |
| Refund Request Line Item | PENDING_APPROVAL | Transitioned from “N/a” |
The following webhook events will fire:
| Webhook Event | Events | Notes |
|---|---|---|
| Refund Request | Create | |
| Refund Request Line Item | Create |
Step 2 Call refundRequestLineItemReturn

The Seller in this case takes the decision that they want the line item returned, so they will make a call to refundRequestLineItemReturn, to do so they will need the RefundRequestLineItem Id of the Line Item you want to return
Note
Please take care to distinguish between theLineItem Id and the RefundRequestLineItem Id. In this example we are working exclusively with RefundRequestLineItem Ids.This call can be made by:
- The Operator
- The Seller
An example mutation call is shown below:
mutation {
refundRequestLineItemReturn(
input: {
refundRequestLineItemId: "UmVmdW5kUmVxdWVzdExpbmVJdGVtLTE2"
notes: [{ note: "Confirm the return of this item is required" }]
}
) {
refundRequestLineItem {
status
}
errors {
field
messages
}
}
}
Following a successful call, our primary objects should have the following state:
| Object | State | Notes |
|---|---|---|
| Order | COMPLETE | |
| Invoice | PAID, SENT, AWAITING_RETURN | |
| Line Item | ALLOCATED | |
| Refund Request | AWAITING | |
| Refund Request Line Item | AWAITING_RETURN | This transitioned from PENDING_APPROVAL |
The following webhook events will fire:
| Webhook Event | Events | Notes |
|---|---|---|
| Refund Request | N/a | |
| Refund Request Line Item | Update |
Step 2a [Optional] Call returnShipmentCreate

The seller can optionally create a return shipment that can including tracking details, attachments etc. To use returnShipmentCreate you will need to pass the refundRequestId. This call can be made by:
- The Operator
- The Seller
Return Shipment Deep Dive
We cover this particular step in more detail in this article, which covers the following additional topics:
- Querying shipment carriers
- Adding url-based attachments
- Adding base 64 encoded attachments
- Updating the status of a return shipment
An example mutation call is shown below:
mutation {
returnShipmentCreate(
input: {
refundRequestId: "UmVmdW5kUmVxdWVzdC0yMA=="
carrierId: "U2hpcG1lbnRDYXJyaWVyLTQ="
trackingNumber: "ABC-1236"
shippedItems: [
{
lineItemId: "UmVmdW5kUmVxdWVzdExpbmVJdGVtLTIw",
quantity: 1
}
]
}
) {
shipment {
id
}
errors {
field
messages
}
}
}
Following a successful call, the statuses of our primary objects do not change:
| Object | State | Notes |
|---|---|---|
| Order | COMPLETE | |
| Invoice | PAID, SENT, AWAITING_RETURN | |
| Line Item | ALLOCATED | |
| Refund Request | AWAITING | |
| Refund Request Line Item | AWAITING_RETURN |
No events for the webhooks we are monitoring will fire either:
| Webhook Event | Events | Notes |
|---|---|---|
| Refund Request | N/a | |
| Refund Request Line Item | N/a |
Step 3 Call refundRequestLineItemAccept

The Seller will now wait until the item has been returned, once it has been and it fulfils the return criteria of the Seller, they would then call refundRequestLineItemAccept, to do so they will need:
- The
RefundRequestLineItemId of the Line Item that has been returned
This call can be made by:
- The Operator
- The Seller
An example mutation call is shown below:
mutation {
refundRequestLineItemAccept(
input: {
refundRequestLineItemId: "UmVmdW5kUmVxdWVzdExpbmVJdGVtLTE3"
notes: [{ note: "Item has been returned in a satisfactory condition" }]
}
) {
refundRequestLineItem {
status
}
errors {
field
messages
}
}
}
Following a successful call, our primary objects should have the following state:
| Object | State | Notes |
|---|---|---|
| Order | COMPLETE | |
| Invoice | PAID, SENT | We no longer have the AWAITING_RETURN status flag |
| Line Item | ALLOCATED | |
| Refund Request | PROCESSED | This transitioned from AWAITING |
| Refund Request Line Item | REFUND_ACCEPTED | This transitioned from AWAITING_RETURN |
The following webhook events will fire:
| Webhook Event | Events | Notes |
|---|---|---|
| Refund Request | status:processed | |
| Update | ||
| Refund Request Line Item | Update |
Step 4 Call refundRequestRefund

Note
This is the final step of our worked example, however we provide an alternate “final step” below (step 4a) using therefundRequestApprove mutation, which as mentioned previously is a more granular replacement for refundRequestRefund.With all of the Sellers steps completed, the Operator now has to finalize the refund by calling refundRequestRefund, to do so they need to supply:
- The
RefundRequestIdId of the refund request
This call can be made by:
- The Operator
An example mutation call is shown below:
mutation {
refundRequestRefund(
input: {
refundRequestId: "UmVmdW5kUmVxdWVzdC0xNw=="
notes: [{ note: "Refund Approved" }]
}
) {
errors {
field
messages
}
refundRequest {
id
status
}
}
}
Following a successful call, our primary objects should have the following state:
| Object | State | Notes |
|---|---|---|
| Order | COMPLETE | |
| Invoice | PAID, SENT, REFUNDED | We have an additional status flag of REFUNDED |
| Line Item | REFUNDED | Transitioned from ALLOCATED |
| Refund Request | REFUNDED | This transitioned from PROCESSED |
| Refund Request Line Item | REFUNDED | This transitioned from REFUND_ACCEPTED |
The following webhook events will fire:
| Webhook Event | Events | Notes |
|---|---|---|
| Refund Request | status:refunded | |
| Update | ||
| Refund Request Line Item | Update |
Step 4a Call refundRequestApprove

Note
This is an alternate approach torefundRequestRefund (Step 4 above), and as mentioned previously refundRequestApprove is available on request only via your Marketplacer Technical Account ManagerIt is assumed that you have followed all the example steps up to and including Step 3 above, with that being the case, the Operator can finalize the refund by calling refundRequestApprove in one of the following 2 ways.
Option 1 - Simplex
This is essentially an identical approach to that used with refundRequestRefund as shown below:
mutation {
refundRequestApprove(input:
{
refundRequestId: "UmVmdW5kUmVxdWVzdC0xNw=="
}
) {
refundRequest{
id
status
}
errors {
field
messages
}
}
}
Calling this mutation will result in the same outcome you would have with calling refundRequestRefund.
Option 2 - Granular
The entire value-proposition of refundRequestApprove is to allow the caller to specify granular values for:
lineItemAmount- The amount to be refunded to the shoppercommissionAmount- The commission component of thelineItemAmountremittanceAmount- The remittance component of thelineItemAmount
You can also optionally specify the tax component of each if required. An example of this approach to calling
refundRequestApproveis shown below:
mutation {
refundRequestApprove(input:
{
refundRequestId: "UmVmdW5kUmVxdWVzdC0xMDk="
lineItemPriceBreakdowns: [
{
id: "UmVmdW5kUmVxdWVzdExpbmVJdGVtLTE0OQ=="
lineItemAmount: 1000
commissionAmount: 200
remittanceAmount: 800
}
]
})
{
refundRequest{
id
status
}
errors{
field
messages
}
}
}
Note
Opting in torefundRequestApprove does change the outputs of the remittance PDFs and add extra fields to GraphQL Invoice Amendments. Otherwise it is backwards compatible with refundRequestRefund.